<?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: Victor Feight</title>
    <description>The latest articles on DEV Community by Victor Feight (@victorfeight).</description>
    <link>https://dev.to/victorfeight</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%2F723246%2F044173b3-b8e3-40d6-95f4-1c9e26d025e7.jpeg</url>
      <title>DEV Community: Victor Feight</title>
      <link>https://dev.to/victorfeight</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victorfeight"/>
    <language>en</language>
    <item>
      <title>A Motivated Example of Variable Length Subnet Masks</title>
      <dc:creator>Victor Feight</dc:creator>
      <pubDate>Mon, 27 Jun 2022 07:05:53 +0000</pubDate>
      <link>https://dev.to/victorfeight/a-motivated-example-of-variable-length-subnet-masks-42gm</link>
      <guid>https://dev.to/victorfeight/a-motivated-example-of-variable-length-subnet-masks-42gm</guid>
      <description>&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=S4k3fGhCEI4"&gt;https://www.youtube.com/watch?v=S4k3fGhCEI4&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is Subnetting?&lt;/li&gt;
&lt;li&gt;How does CIDR work?&lt;/li&gt;
&lt;li&gt;Motivation for VLSM 1: Static Length Subnet masks&lt;/li&gt;
&lt;li&gt;How VLSM Works&lt;/li&gt;
&lt;li&gt;Designing a VLSM&lt;/li&gt;
&lt;li&gt;Designing a  VLSM Example&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Subnetting?
&lt;/h2&gt;

&lt;p&gt;Hey everyone, I'm taking a small detour from my Dokku series to play with some numbers. 🦓&lt;/p&gt;

&lt;p&gt;In this blog post, I'll try and teach you all the tricks I've struggled to learn with variable length subnet masks through a motivated example.&lt;/p&gt;

&lt;p&gt;But first, what is &lt;strong&gt;subnetting?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Subnetting helps us break large networks into smaller network segments, allowing network traffic to travel a shorter distance without passing through unnecessary router hops on the way to its destination.&lt;/p&gt;

&lt;p&gt;This eases network congestion and reduces network load, &lt;em&gt;increasing network efficiency.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does CIDR work?
&lt;/h2&gt;

&lt;p&gt;Classless Inter-Domain Routing (CIDR) is a subnetting notation used in modern networking. CIDR helps an IP address encapsulate both total network count and total host count for the network range in one address, determined by the &lt;strong&gt;network bits (1s)&lt;/strong&gt; and the following &lt;strong&gt;host bits (0s)&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/8 = 255.0.0.0  (ie first 8 bits of subnet mask are one—11111111)
/16 = 255.255.0.0
/24 = 255.255.255.0
/32 = 255.255.255.255
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works like this, say we have an IP address, &lt;code&gt;172.16.0.0/24&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In decimal, that's &lt;code&gt;255.255.255.0.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/24&lt;/code&gt; means that the first 24 bits of the &lt;strong&gt;network bits&lt;/strong&gt; are turned on in the subnet mask.&lt;/p&gt;

&lt;p&gt;ie. &lt;code&gt;11111111.11111111.11111111.00000000&lt;/code&gt; indicate 24 network bits (set), and 8 host bits (unset). Network bits on the left, host bits on the right.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is 2&lt;sup&gt;n&lt;/sup&gt; networks, where &lt;em&gt;n&lt;/em&gt;  is number of network bits, resulting in 16,777,216 networks. &lt;/li&gt;
&lt;li&gt;The number of hosts is 2&lt;sup&gt;h&lt;/sup&gt;, where &lt;em&gt;h&lt;/em&gt; is the number of host bits, resulting in 256 hosts per network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Who's that subnet?&lt;/em&gt;&lt;/strong&gt;:&lt;br&gt;
In decimal, that's &lt;code&gt;255.255.255.0&lt;/code&gt;. Familiar? That's because it was also used into old Classful addressing, which is still used in Windows IP protocol settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IFN4g_PF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IFN4g_PF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted5.png" alt="placeholder" width="401" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation for VLSM 1: Static Length Subnet masks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--leBzpzjC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--leBzpzjC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_2.png" alt="placeholder" width="880" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the subnet mask is the same for every subnet, usable IPs become wasted. That's how old Classful IP addressing used to work before CIDR.&lt;/p&gt;

&lt;p&gt;Across this whole network a mask of &lt;code&gt;/24&lt;/code&gt; is used, resulting in &lt;code&gt;254&lt;/code&gt; usable hosts per subnet. Note that in every single subnet, some host addresses will remain unused. In particular, 252 host addresses are wasted for each Point to Point WAN Link between the routers. &lt;/p&gt;

&lt;p&gt;Let's calculate it: Altogether, we are using 201 + 201 + 51(4) + 2 + 2 + 2 + 2 addresses (add 1 to each LAN segment since it's connected to router interface) = 612 used addresses.&lt;/p&gt;

&lt;p&gt;The total allotted using &lt;code&gt;/24&lt;/code&gt; across the network would be 254(6) for the LANs + 254(4) for each of the WAN links = 2,540 total alloted.&lt;/p&gt;

&lt;p&gt;Then the &lt;strong&gt;wasted addresses&lt;/strong&gt; would be 1,928 IP addresses.&lt;/p&gt;

&lt;p&gt;That's a lot of addresses for traffic to flow between, with may result in many unnecessary routes between router hops, resulting in &lt;strong&gt;network congestion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calculating Usable IPs&lt;/strong&gt;:&lt;br&gt;
Usable IP address formula is 2&lt;sup&gt;h&lt;/sup&gt; - 2, where h = number of host bits. This is because we subtract the 2 unusable IPs, Network IP and Broadcast IP.&lt;br&gt;
&lt;code&gt;172.16.0.0/24&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Would leave 8 host bits. 2&lt;sup&gt;8&lt;/sup&gt; - 2 = 254 usable IPs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How VLSM Works
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ctljrSJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ctljrSJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_1.png" alt="placeholder" width="880" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using VLSM, each LAN and WAN point to point Link can have a different Subnet mask. You may have guessed the advantage to this: LANs with fewer required hosts can have their own subnet.&lt;/p&gt;

&lt;p&gt;Now we can have each LAN segment behind HQ router use &lt;code&gt;/24&lt;/code&gt; mask, resulting in 254 usable IPs, so only &lt;code&gt;254 usable IPs - 201 addresses used = 53 IPs&lt;/code&gt; wasted per LAN (if you include the router interface in the addresses used).&lt;/p&gt;

&lt;p&gt;For branch LANs behind routers A through D, &lt;code&gt;/26&lt;/code&gt; results in 62 usable IPs, so only (62 - 51) = 11 IPs wasted per LAN.&lt;/p&gt;

&lt;p&gt;For each WAN Link, &lt;code&gt;/30&lt;/code&gt; results in 2 usable IPs, so only two IPs are needed for Serial Point to Point WAN Links (one for each router interface link). This means 8 usable IPs, one for each of the linked router interface pairs, resulting in no wasted IPs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing a VLSM
&lt;/h2&gt;

&lt;p&gt;The general steps to designing a VLSM for a network is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Arrange the network from largest to smallest&lt;/li&gt;
&lt;li&gt;Use the chart to determine mask, and divide the address into equal address blocks (&lt;em&gt;subnetting&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Note the remaining subnet numbers from mask in step 2.&lt;/li&gt;
&lt;li&gt;Take the next available subnet, and subnet it further for smaller networks.&lt;/li&gt;
&lt;li&gt;Write down new subnet numbers again.&lt;/li&gt;
&lt;li&gt;Repeat steps 4-5 for smaller segments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KBcLlcdF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBcLlcdF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted1.png" alt="placeholder" width="880" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing a  VLSM Example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TD416_TP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TD416_TP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted2.png" alt="placeholder" width="797" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first step will be to arrange the networks from largest to smallest:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The network in order of host-size will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LAN-D is &lt;strong&gt;the largest&lt;/strong&gt; with 29 hosts behind Router D&lt;/li&gt;
&lt;li&gt;LAN-B with 20 hosts behind Router B, LAN-C with 20 hosts behind Router C&lt;/li&gt;
&lt;li&gt;LAN-A with 13 hosts behind Router A&lt;/li&gt;
&lt;li&gt;WAN Link A &amp;lt;--&amp;gt; D with 2 hosts, WAN Link B &amp;lt;--&amp;gt; D with 2 hosts, WAN Link C &amp;lt;--&amp;gt; D with 2 hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Note that &lt;strong&gt;LAN-D&lt;/strong&gt; is the largest with 30 hosts (29 hosts + 1 router interface). Checking the chart, we can see that as we require 30 hosts, &lt;code&gt;/27&lt;/code&gt; would provide us with 2&lt;sup&gt;h&lt;/sup&gt; - 2 = 2&lt;sup&gt;8&lt;/sup&gt; - 2 = 30 usable addresses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Therefore, we'll assign &lt;code&gt;192.168.1.0/27&lt;/code&gt; to LAN-D segment. With this &lt;code&gt;/27&lt;/code&gt; network we'll have a subnet mask of &lt;code&gt;255.255.255.224/27&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's &lt;code&gt;11111111.11111111.11111111.11100000&lt;/code&gt;, with 5 unset host-bits, that's 2&lt;sup&gt;5&lt;/sup&gt; = 32 hosts possible per subnet.&lt;/p&gt;

&lt;p&gt;256 - 224 = 32 (magic number)&lt;br&gt;
&lt;code&gt;0 32&lt;/code&gt;  =&amp;gt; NW IP  &lt;code&gt;192.168.1.0&lt;/code&gt;, Broadcast IP: &lt;code&gt;192.168.1.31&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thus, our subnet possibilities for &lt;code&gt;192.168.1.0/27&lt;/code&gt; are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;192.168.1.0/27&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.32/27&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.64/27&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.96/27&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.128/27&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;....etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ROUTER D LAN:&lt;/strong&gt;&lt;br&gt;
Number of Hosts – 30&lt;br&gt;
Host Bits – 5 bits&lt;br&gt;
Subnet Mask – &lt;code&gt;/27 or 255.255.255.224&lt;/code&gt;&lt;br&gt;
Increment – 32&lt;br&gt;
Network Address – &lt;code&gt;192.168.1.0&lt;/code&gt;&lt;br&gt;
Broadcast Address – &lt;code&gt;192.168.1.31&lt;/code&gt;&lt;br&gt;
Usable IP Addresses – &lt;code&gt;192.168.1.1 to 192.168.1.30&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yVr2scto--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yVr2scto--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted3.png" alt="placeholder" width="880" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Note that the next smallest subnets, LAN-B and LAN-C attached to Router-B and Router-C both require 21 host addresses (20 hosts + 1 router interface each). Going by the chart, the smallest possible block-size for 21 hosts is 32, which is a &lt;code&gt;/27&lt;/code&gt; subnet mask.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We already have two subnets with &lt;code&gt;/27&lt;/code&gt; available from step 2. Assign them to LAN-B and LAN-C subnets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;192.168.1.32/27&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.64/27&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ROUTER B LAN:&lt;/strong&gt;&lt;br&gt;
256 - 224 = 32 (magic number)&lt;br&gt;
&lt;code&gt;32 64&lt;/code&gt;  =&amp;gt; NW IP  &lt;code&gt;192.168.1.32&lt;/code&gt;, Broadcast IP: &lt;code&gt;192.168.1.63&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Number of Hosts – 21&lt;br&gt;
Host Bits – 5 bits&lt;br&gt;
Subnet Mask – &lt;code&gt;/27 or 255.255.255.224&lt;/code&gt;&lt;br&gt;
Increment – 32&lt;br&gt;
Network Address – &lt;code&gt;192.168.1.32&lt;/code&gt;&lt;br&gt;
Broadcast Address – &lt;code&gt;192.168.1.63&lt;/code&gt;&lt;br&gt;
Usable IP Addresses – &lt;code&gt;192.168.1.33 to 192.168.1.62&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ROUTER C LAN:&lt;/strong&gt;&lt;br&gt;
256 - 224 = 32 (magic number)&lt;br&gt;
&lt;code&gt;64 96&lt;/code&gt;  =&amp;gt; NW IP  &lt;code&gt;192.168.1.64&lt;/code&gt;, Broadcast IP: &lt;code&gt;192.168.1.95&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Number of Hosts – 30&lt;br&gt;
Host Bits – 5 bits&lt;br&gt;
Subnet Mask – &lt;code&gt;/27 or 255.255.255.224&lt;/code&gt;&lt;br&gt;
Increment – 32&lt;br&gt;
Network Address – &lt;code&gt;192.168.1.64&lt;/code&gt;&lt;br&gt;
Broadcast Address – &lt;code&gt;192.168.1.95&lt;/code&gt;&lt;br&gt;
Usable IP Addresses – &lt;code&gt;192.168.1.65 to 192.168.1.94&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The next smallest subnet is LAN-A with 14 host addresses (13 hosts + 1 router interface). From the chart, we can see the smallest block size is 16, so we can use subnet mask &lt;code&gt;/28&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Take the next available subnet from &lt;code&gt;/27&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;192.168.1.0/27&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;192.168.1.32/27&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;192.168.1.64/27&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;192.168.1.96/27&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subnet &lt;code&gt;192.168.1.96&lt;/code&gt; further using the &lt;code&gt;/28&lt;/code&gt; mask, resulting in two subnets:  &lt;strong&gt;192.168.1.96/28&lt;/strong&gt;, &lt;strong&gt;192.168.1.112/28&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding the number of Subnets&lt;/strong&gt;:&lt;br&gt;
Recall prior &lt;code&gt;192.168.1.0/27&lt;/code&gt; binary subnet: 11111111.111111111.11111111.&lt;strong&gt;11100000&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;192.168.1.96/27&lt;/code&gt; to &lt;code&gt;/28&lt;/code&gt; means 1 host bit was borrowed (111 to 1111), and the formua for number of subnets is 2&lt;sup&gt;s, where s is host bits borrowed.&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Thus 2&lt;sup&gt;1&lt;/sup&gt; = 2 subnets.&lt;/p&gt;

&lt;p&gt;We assign &lt;em&gt;the first available&lt;/em&gt; subnet to LAN-A segment (&lt;code&gt;192.168.1.96&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oRWt-r3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oRWt-r3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/VLSM_Pasted4.png" alt="placeholder" width="622" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write down the new subnet numbers again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our two subnets would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;192.168.1.96&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;192.168.1.112&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ROUTER B LAN:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;192.168.1.96/28&lt;/code&gt; means 11111111.11111111.11111111.&lt;strong&gt;11110000&lt;/strong&gt; or 240 subnet mask.  256 – 240 = 16 (magic number). &lt;br&gt;
&lt;code&gt;96 112&lt;/code&gt;  =&amp;gt; NW IP  &lt;code&gt;192.168.1.96&lt;/code&gt;, Broadcast IP: &lt;code&gt;192.168.1.111&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Number of Hosts – 14&lt;br&gt;
Host Bits – 4 bits&lt;br&gt;
Subnet Mask – &lt;code&gt;/28 or 255.255.255.240&lt;/code&gt;&lt;br&gt;
Increment – 16&lt;br&gt;
Network Address – &lt;code&gt;192.168.1.96&lt;/code&gt;&lt;br&gt;
Broadcast Address – &lt;code&gt;192.168.1.111&lt;/code&gt;&lt;br&gt;
Usable IP Addresses – &lt;code&gt;192.168.1.33 to 192.168.1.62&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Repeat steps 4-5 for the three point-to-point WAN segments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The smallest segments are the three WAN links between the routers (2 hosts each):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WAN Link A &amp;lt;--&amp;gt; D with 2 hosts&lt;/li&gt;
&lt;li&gt;WAN Link B &amp;lt;--&amp;gt; D with 2 hosts&lt;/li&gt;
&lt;li&gt;WAN Link C &amp;lt;--&amp;gt; D with 2 hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We know that each of these WANs' address block sizes will need to accommodate 2 usable host IPs (for both router interfaces) &lt;em&gt;plus&lt;/em&gt;  2 (the network IP and the broadcast ip).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is because 2&lt;sup&gt;h&lt;/sup&gt; - 2 = 2&lt;sup&gt;2&lt;/sup&gt; - 2 = 2 usable IPs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, 2 hosts requires block size 4, and thus subnet mask &lt;code&gt;/30&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Available subnets:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;192.168.1.96&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;192.168.1.112&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take the next available subnet: &lt;code&gt;192.168.1.112/28&lt;/code&gt; and subnet it further with a &lt;code&gt;/30&lt;/code&gt; mask.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;192.168.1.112/30&lt;/code&gt; has 2&lt;sup&gt;h&lt;/sup&gt; = 2&lt;sup&gt;2&lt;/sup&gt; hosts per subnet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;11111111.11111111.11111111.&lt;strong&gt;11110000 (/28)&lt;/strong&gt;&lt;br&gt;
11111111.11111111.11111111.&lt;strong&gt;11111100 (/30)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 host bits borrowed&lt;/strong&gt; gives 2&lt;sup&gt;s&lt;/sup&gt; = 2&lt;sup&gt;2&lt;/sup&gt; = 4 subnets possible.&lt;/p&gt;

&lt;p&gt;We'll take the next 3 for our WAN Link Segments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;192.168.1.112/30&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;192.168.1.116/30&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;192.168.1.120/30&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;192.168.1.124/30&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;WAN Link A &amp;lt;--&amp;gt; D&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;192.168.1.112/30&lt;/code&gt;
&lt;code&gt;11111111.11111111.11111111.11111100&lt;/code&gt; =&amp;gt; &lt;code&gt;255.255.255.252/30&lt;/code&gt;
256 - 252 = 4 (magic number)
&lt;code&gt;112       116&lt;/code&gt; =&amp;gt; NW IP &lt;code&gt;192.168.1.112/30&lt;/code&gt;, Broadcast IP: &lt;code&gt;192.168.1.115&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Number of Hosts – 2&lt;br&gt;
Host Bits – 2 bits&lt;br&gt;
Subnet Mask – &lt;code&gt;/30 or 255.255.255.252&lt;/code&gt;&lt;br&gt;
Increment – 4&lt;br&gt;
Network Address – &lt;code&gt;192.168.1.112&lt;/code&gt;&lt;br&gt;
Broadcast Address – &lt;code&gt;192.168.1.115&lt;/code&gt;&lt;br&gt;
Usable IP Addresses – &lt;code&gt;192.168.1.113 to 192.168.1.114&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WAN Link B &amp;lt;--&amp;gt; D&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;192.168.1.116/30&lt;/code&gt;
&lt;code&gt;116       120&lt;/code&gt; =&amp;gt; NW IP &lt;code&gt;192.168.1.116/30&lt;/code&gt;, Broadcast IP: &lt;code&gt;192.168.1.119&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Number of Hosts – 2&lt;br&gt;
Host Bits – 2 bits&lt;br&gt;
Subnet Mask – &lt;code&gt;/30 or 255.255.255.252&lt;/code&gt;&lt;br&gt;
Increment – 4&lt;br&gt;
Network Address – &lt;code&gt;192.168.1.116&lt;/code&gt;&lt;br&gt;
Broadcast Address – &lt;code&gt;192.168.1.119&lt;/code&gt;&lt;br&gt;
Usable IP Addresses – &lt;code&gt;192.168.1.117 to 192.168.1.118&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WAN Link C &amp;lt;--&amp;gt; D&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;192.168.1.120/30&lt;/code&gt;
&lt;code&gt;120       124&lt;/code&gt; =&amp;gt; NW IP &lt;code&gt;192.168.1.120/30&lt;/code&gt;, Broadcast IP: &lt;code&gt;192.168.1.123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Number of Hosts – 2&lt;br&gt;
Host Bits – 2 bits&lt;br&gt;
Subnet Mask – &lt;code&gt;/30 or 255.255.255.252&lt;/code&gt;&lt;br&gt;
Increment – 4&lt;br&gt;
Network Address – &lt;code&gt;192.168.1.120&lt;/code&gt;&lt;br&gt;
Broadcast Address – &lt;code&gt;192.168.1.123&lt;/code&gt;&lt;br&gt;
Usable IP Addresses – &lt;code&gt;192.168.1.121 to 192.168.1.122&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We've broken down our subnet from a &lt;code&gt;/27&lt;/code&gt; to a &lt;code&gt;/28&lt;/code&gt; and then to a &lt;code&gt;/30&lt;/code&gt;. By optimizing our subnetting with VLSM, we can summarize results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LAN-D is &lt;strong&gt;the largest&lt;/strong&gt; with 29 hosts behind Router D&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LAN-D requires 30 hosts (29 hosts in LAN segment, 1 for router interface)&lt;br&gt;
With &lt;code&gt;/27&lt;/code&gt; it has 2&lt;sup&gt;5&lt;/sup&gt; - 2 usable hosts, giving 31 hosts (30 usable hosts + 1 for router interface). Thus there is &lt;strong&gt;1 wasted IP address&lt;/strong&gt; on LAN-D.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LAN-B with 20 hosts behind Router B, LAN-C with 20 hosts behind Router C&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LAN-B requires 21 hosts (20 hosts in LAN segment, 1 for router interface). &lt;code&gt;/27&lt;/code&gt; gives 31 hosts (30 usable hosts + 1 for router interface), . Thus, in LAN-B and LAN-C, there are &lt;em&gt;only 10 wasted IP addresses&lt;/em&gt; each.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LAN-A with 13 hosts behind Router A&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LAN-A requires 14 hosts (13 in LAN segment, 1 for router interface). &lt;code&gt;/28&lt;/code&gt; gives 15 hosts (14 usable hosts + 1 router interface). Thus there is &lt;strong&gt;1 wasted IP address on LAN-D&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WAN Link A &amp;lt;--&amp;gt; D uses 2 hosts (as does WAN Link B &amp;lt;--&amp;gt; D and WAN Link C &amp;lt;--&amp;gt; D)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WAN Link A &amp;lt;--&amp;gt; D requires 2 hosts (one at each router interface in the Point to Point WAN Link). &lt;code&gt;/30&lt;/code&gt; gives 2 usable hosts. Thus there are &lt;strong&gt;no wasted IP addresses on any WAN links&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Altogether, we have 92 addresses being used (30 + 21 + 21 + 14 + 2 + 2 + 2).&lt;br&gt;
We have 114 addresses total allocated (15 + 31 + 31 + 31 + 2 + 2 + 2).&lt;/p&gt;

&lt;p&gt;Thus, 114 - 92 = &lt;strong&gt;22 addresses&lt;/strong&gt; wasted. Big improvement over 1,928.&lt;/p&gt;

&lt;p&gt;Hope you enjoyed, and hope to be back soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;a href="https://isrdoc.files.wordpress.com/2010/11/subnetting-vlsm.pdf"&gt;https://isrdoc.files.wordpress.com/2010/11/subnetting-vlsm.pdf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://www.comparitech.com/net-admin/variable-length-subnet-mask-vlsm-tutorial/"&gt;https://www.comparitech.com/net-admin/variable-length-subnet-mask-vlsm-tutorial/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>networks</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>math</category>
    </item>
    <item>
      <title>Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 4</title>
      <dc:creator>Victor Feight</dc:creator>
      <pubDate>Fri, 17 Jun 2022 01:58:34 +0000</pubDate>
      <link>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-4-37ag</link>
      <guid>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-4-37ag</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Sidequest: Installing NVM in Powershell&lt;/li&gt;
&lt;li&gt;Local Strapi backend Installation&lt;/li&gt;
&lt;li&gt;Configuring our Local Strapi Backend&lt;/li&gt;
&lt;li&gt;Install GraphQL plugin and configure it locally&lt;/li&gt;
&lt;li&gt;Creating a Collection Type for Articles&lt;/li&gt;
&lt;li&gt;Set roles and permissions on our Strapi API&lt;/li&gt;
&lt;li&gt;Database configuration with .env variables&lt;/li&gt;
&lt;li&gt;Testing the Strapi API Locally via GraphQL&lt;/li&gt;
&lt;li&gt;Still to Come...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Welcome back!&lt;/p&gt;

&lt;p&gt;╰(&lt;em&gt;°▽°&lt;/em&gt;)╯&lt;/p&gt;

&lt;p&gt;If you've been following along so far, you should have a Dokku instance on DigitalOcean that's been hardened from attackers (through &lt;a href="https://victorfeight.com/2022/03/secure_dokku_droplet-2/"&gt;SSHD configuration and Fail2Ban static IP allow-lists&lt;/a&gt;). You should also have &lt;a href="https://victorfeight.com/2022/05/secure_dokku_droplet-3/"&gt;a MySQL database server &lt;/a&gt; installed onto the instance. ✔&lt;/p&gt;

&lt;p&gt;In this post, we'll be installing Strapi CMS onto our local machine, and then allow Strapi to talk to our local MySQL instance. &lt;strong&gt;Strapi&lt;/strong&gt; is an open-source content management system that eases managing a custom blog by enabling multiple users and fine-grained permissions over adding, editing, or deleting posts.&lt;/p&gt;

&lt;p&gt;Once it's set up, it's as easy as making a new post with your username through Strapi's dashboard, and the file will get posted into our database automatically. We can turn on the GraphQL Playground and start querying for our posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;INFO:&lt;/strong&gt; Hostname is blocked by MySQL&lt;br&gt;
Recently, I had an error when trying to connect to my remote mysql instance via MySQL Workbench.&lt;br&gt;
The error was along the lines of: Host is blocked because of many connection errors.&lt;/p&gt;

&lt;p&gt;At this point, I ssh'd into my machine with &lt;code&gt;ssh vic@apps.vicstech.xyz&lt;/code&gt;, entered my dokku db instance with &lt;code&gt;dokku mysql:enter db&lt;/code&gt; and while in the dokku db bash instance, entered &lt;code&gt;mysql -u root -p&lt;/code&gt; followed by my db password.&lt;/p&gt;

&lt;p&gt;Once in, I ran &lt;code&gt;FLUSH HOSTS;&lt;/code&gt; to empty the host table. Apparently this happens if more than max_connect_errors occur successively for a given host, MySQL will block them. By flushing the tables, further connection attempts from the host are allowed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sidequest: Installing NVM in Powershell
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; Strapi and Node versions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strapi won't run on NodeJS odd numbered versions - which are for developers. If you're not using LTS, you can temporarily change to use 14.16.1 with &lt;code&gt;nvm use 14.16.1&lt;/code&gt; in an elevated powershell (assuming &lt;a href="https://github.com/nvm-sh/nvm"&gt;nvm&lt;/a&gt; is installed).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zoFj7tft--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615034520.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zoFj7tft--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615034520.png" alt="placeholder" width="561" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; The easiest way to install &lt;code&gt;nvm&lt;/code&gt; (Node Version manager) is to use Chocolatey. 🍫&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right Click Powershell icon in Start menu, select &lt;strong&gt;Run As Administrator.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Confirm chocolatey is installed by running &lt;code&gt;choco&lt;/code&gt; command in a new elevated Powershell.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lHEooFrl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615160234.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHEooFrl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615160234.png" alt="placeholder" width="622" height="67"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If it's not installed, run the following command: &lt;code&gt;Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install &lt;code&gt;nvm&lt;/code&gt; using &lt;code&gt;choco&lt;/code&gt; by running: &lt;code&gt;choco -y nvm&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open a &lt;strong&gt;new&lt;/strong&gt;, &lt;em&gt;regular&lt;/em&gt; Powershell and install Node.js using nvm: &lt;code&gt;nvm install 14.16.1&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Great. We'll need to use Node 14.16.1 LTS in Powershell to ensure optimal Strapi and node compatibility.&lt;/p&gt;
&lt;h2&gt;
  
  
  Local Strapi backend Installation
&lt;/h2&gt;

&lt;p&gt;Create a new directory in your project and name is &lt;code&gt;strapi-dokku-backend&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We're going to download the latest Strapi code onto our local machine before pushing it to our dokku instance.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;&lt;em&gt;powershell&lt;/em&gt;&lt;/strong&gt; with node 14.16.1 enabled, run npm/npx to install the Strapi project named 'strapi-backend' &lt;em&gt;locally&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-strapi-app@latest strapi-backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Choose manual Installation with arrow keys, press Enter&lt;/li&gt;
&lt;li&gt;Choose mysql as default database client&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;db&lt;/code&gt; as Database name.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;localhost&lt;/code&gt; as Host.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;3306&lt;/code&gt; as Port.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;root&lt;/code&gt; as username&lt;/li&gt;
&lt;li&gt;Enter &lt;strong&gt;password&lt;/strong&gt; used for local and remote root MySQL instances.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;N&lt;/code&gt; for "Enable SSL connection" option&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;INFO:&lt;/strong&gt; You can retrieve this password by sshing into &lt;code&gt;apps.vicstech.xyz&lt;/code&gt; and running: &lt;code&gt;sudo cat /var/lib/dokku/services/mysql/db/ROOTPASSWORD&lt;/code&gt; on your Dokku instance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you've been following along, this password should now be associated with both the root user in your &lt;em&gt;local MySQL Workbench&lt;/em&gt;, as well as the root user of our &lt;em&gt;remote MySQl instance&lt;/em&gt; on the Dokku Digital Ocean droplet. If you entered something different as a local password, ensure DATABASE_PASSWORD in env is set to your remote instance ROOTPASSWORD, as that will take precedence in settings when Strapi is pushed onto dokku.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, run the following commands in the newly created strapi-backend directory:&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;cd &lt;/span&gt;strapi-backend
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;strapi-admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Powershell Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iH4A6trk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615154625.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iH4A6trk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615154625.png" alt="placeholder" width="880" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Strapi command reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available commands in your project:

npm run develop
# Start Strapi in watch mode. (Changes in Strapi project files will trigger a server restart)

npm run start
# Start Strapi without watch mode.

npm run build
# Build Strapi admin panel.

npm run strapi
# Display all available commands.

# You can start by doing:
cd /mnt/c/Users/Vic/Documents/CODE_2020/ELEVENTY/strapi-dokku-backend/backend
npm run develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring our Local Strapi Backend
&lt;/h2&gt;

&lt;p&gt;We can go ahead and give it a whirl by running the following commands in the Strapi &lt;code&gt;strapi-backend&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went smoothly, you should get a &lt;em&gt;Windows Firewall notification&lt;/em&gt; asking to allow Strapi permission to serve its admin panel. Go ahead and &lt;strong&gt;allow&lt;/strong&gt; it.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--efCbP7RZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615155333.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--efCbP7RZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615155333.png" alt="placeholder" width="880" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As per &lt;a href="https://docs.strapi.io/developer-docs/latest/getting-started/quick-start.html"&gt;Strapi developer docs&lt;/a&gt; quick start guide:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once the installation is complete, your browser automatically opens a new tab.&lt;br&gt;
By completing the form, you create your own account. Once done, you become the first administator user of this Strapi application. Welcome aboard, commander!&lt;br&gt;
🥳&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Complete this form to create the first &lt;strong&gt;Administrator&lt;/strong&gt; user and click &lt;strong&gt;Let's Start!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SKbWzGYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615191600.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SKbWzGYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615191600.png" alt="placeholder" width="505" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you'll have access to the Strapi &lt;strong&gt;admin panel&lt;/strong&gt;. Navigate to &lt;code&gt;http://localhost:1337/admin&lt;/code&gt; in your browser and you should see a success message:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jYtmaygE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615155911.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jYtmaygE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615155911.png" alt="placeholder" width="834" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we've got a local Strapi server running (&lt;code&gt;strapi-backend&lt;/code&gt;), configured to use MySQL with a database named &lt;code&gt;db&lt;/code&gt; through port 3306.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install GraphQL plugin and configure it locally
&lt;/h2&gt;

&lt;p&gt;We're going to install the GraphQL playground plugin and configure it to be used both locally or remotely.&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;Ctrl-C&lt;/code&gt; to cancel any running Strapi instance, then run the following in your &lt;code&gt;strapi-backend&lt;/code&gt; project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run strapi &lt;span class="nb"&gt;install &lt;/span&gt;graphql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;Next, create a &lt;code&gt;plugins.js&lt;/code&gt; file at &lt;code&gt;/config/plugins.js&lt;/code&gt; (in your root &lt;code&gt;strapi-backend&lt;/code&gt; directory):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// path: ./config/plugins.js&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//&lt;/span&gt;
  &lt;span class="na"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;shadowCRUD&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="na"&gt;playgroundAlways&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="na"&gt;depthLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;amountLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;apolloServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;introspection&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will enable Strapi's &lt;strong&gt;GraphQL playground&lt;/strong&gt; both locally and remotely (by default, it's turned off when deployed remotely), and set its endpoint to &lt;code&gt;/graphql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HZkYWo2_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615163643.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HZkYWo2_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615163643.png" alt="placeholder" width="880" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, go ahead and rebuild Strapi with GraphQL installed, and run in develop mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
npm run develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a Collection Type for Articles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;INFO:&lt;/strong&gt; Develop Mode&lt;br&gt;
Develop mode allows us to add new collection types, and add fields to these new collection types.&lt;/p&gt;

&lt;p&gt;We're going to create a data structure for our future blog content, add some entries and publish them so that the API for the content can be consumed. We do this by first creating collection types for &lt;code&gt;Article&lt;/code&gt; with the content-type builder and create the fields to display when adding new entries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Content-Types Builder&lt;/strong&gt; under Plugins in the left-hand menu.&lt;/li&gt;
&lt;li&gt;Click the "&lt;strong&gt;+ Create new collection type&lt;/strong&gt;" link&lt;/li&gt;
&lt;li&gt;Name it &lt;strong&gt;Article&lt;/strong&gt; and click continue&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Text field (short text)&lt;/strong&gt; and name it &lt;strong&gt;title&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the "+ Add another field" button&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Rich Text field&lt;/strong&gt; and name it &lt;strong&gt;content&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the "+ Add another field" button&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Date field (date)&lt;/strong&gt; and name it &lt;strong&gt;date&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the "+ Add another field" button&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Text field (short text)&lt;/strong&gt; and name it &lt;strong&gt;author&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the "+ Add another field" button&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Text field (short text)&lt;/strong&gt; and name it &lt;strong&gt;excerpt&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the "+ Add another field" button&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Text field (short text)&lt;/strong&gt; and name it &lt;strong&gt;categories&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the "&lt;strong&gt;Finish&lt;/strong&gt;" button&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Save&lt;/strong&gt; button and wait for Strapi to restart&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tYzDOBYH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615234116.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tYzDOBYH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615234116.png" alt="placeholder" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information, see the &lt;a href="https://strapi.io/blog/building-a-blog-with-11ty-and-strapi"&gt;Strapi Quick Start&lt;/a&gt; guide.&lt;/p&gt;
&lt;h2&gt;
  
  
  Set roles and permissions on our Strapi API
&lt;/h2&gt;

&lt;p&gt;Next, we'll set roles and permission on our API and and then test querying our new posts in GraphQL.&lt;/p&gt;

&lt;p&gt;Click Content Manager on the left menu.&lt;br&gt;
Navigate to Article under Collection Types&lt;/p&gt;

&lt;p&gt;Click "&lt;strong&gt;Create New Entry&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;Fill in this information in the fields specified :&lt;br&gt;
&lt;strong&gt;title&lt;/strong&gt;: This is my lovely title&lt;br&gt;
&lt;strong&gt;content&lt;/strong&gt;: Hope you enjoy this great content.&lt;br&gt;
&lt;strong&gt;author&lt;/strong&gt;: vrfeight&lt;br&gt;
&lt;strong&gt;data&lt;/strong&gt; &lt;em&gt;pick a date of your choice&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;exerpt&lt;/strong&gt;: Short description&lt;br&gt;
&lt;strong&gt;categories&lt;/strong&gt;: test&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order to send requests to the Strapi API, we need public permissions on our articles to make them accessible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Settings&lt;/strong&gt; under &lt;strong&gt;General&lt;/strong&gt; left menu.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Roles&lt;/strong&gt; under &lt;strong&gt;Users &amp;amp; Permissions Plugin&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Public Role&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Scroll down under Permissions, find &lt;strong&gt;Articles&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Tick the &lt;strong&gt;find&lt;/strong&gt; and &lt;strong&gt;findone&lt;/strong&gt; boxes.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UICLMwp---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://victorfeight.com/img/firefox_pJMAOi4ART.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UICLMwp---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://victorfeight.com/img/firefox_pJMAOi4ART.gif" alt="placeholder" width="880" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a collection type with content, and permissions on the articles, we can finally test querying our API. 🎉&lt;/p&gt;
&lt;h2&gt;
  
  
  Database configuration with .env variables
&lt;/h2&gt;

&lt;p&gt;We'll finish setting everything up locally and confirm that local MySQL database querying works in the GraphQL playground.&lt;/p&gt;

&lt;p&gt;Now, the next part's important, so sip your tea and roll up your sleeves. 🍵👕&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; Strapi local .env to remote settings&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strapi uses an environment variable for Host, Port, APP_KEYS, and JWT_SECRET. When pushed to dokku, all these environment variables can be processed once configured on the server for our strapi-backend application, in addition to any other variables set in the &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our authentication strategy is based on the use of &lt;strong&gt;Users &amp;amp; Permissions plugin&lt;/strong&gt; API access.&lt;/p&gt;

&lt;p&gt;You will see in the &lt;code&gt;database.js&lt;/code&gt; file's &lt;code&gt;module.exports&lt;/code&gt; function there is a &lt;code&gt;connection&lt;/code&gt; property holding an object with these configuration values (password will be unique, set upon Strapi installation):&lt;br&gt;
&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;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;env&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="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mysql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// host: env('DATABASE_HOST', 'localhost' ),&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_HOST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_PORT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_NAME&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_USERNAME&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e754d62fcfk5f929&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_SSL&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&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;The way this works, is that the local Strapi server will check the first argument value (ie &lt;code&gt;DATABASE_PORT&lt;/code&gt; or &lt;code&gt;DATABASE_HOST&lt;/code&gt;), and if these values are not found in the &lt;code&gt;.env&lt;/code&gt; file, then use the second argument provided (&lt;code&gt;3306&lt;/code&gt; or &lt;code&gt;localhost&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;A default Strapi &lt;code&gt;.env&lt;/code&gt; file might look like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3EzWblvP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615024355.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3EzWblvP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220615024355.png" alt="placeholder" width="878" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, we can start by testing our local server with this default &lt;code&gt;.env&lt;/code&gt; file, and afterward add database variables in &lt;code&gt;.env&lt;/code&gt; with information from our remote mysql server.&lt;/p&gt;

&lt;p&gt;This will allow us to query from either a local database (MySQL Workbench) or our remote MySQL instance by commenting or uncommenting Database variables in &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; Remote Dokku-mysql environment vars&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Later, when we push Strapi onto our remote dokku instance, we will use &lt;code&gt;dokku config&lt;/code&gt; to set these same environment variables remotely, so that Strapi can pick up our remote dokku-mysql database. 😎&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Testing the Strapi API Locally via GraphQL
&lt;/h2&gt;

&lt;p&gt;To query your data, navigate to &lt;code&gt;http://localhost:1337/api/articles/1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should get back a JSON response:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y_S9unnm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220616014723.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y_S9unnm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220616014723.png" alt="placeholder" width="445" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to &lt;a href="http://localhost:1337/graphql"&gt;http://localhost:1337/graphql&lt;/a&gt;&lt;br&gt;
Welcome to your personal GraphQL Playground. 🙂&lt;/p&gt;

&lt;p&gt;Try this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Write your query or mutation here&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nZifAor---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220616022819.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nZifAor---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/fourth-post-20220616022819.png" alt="placeholder" width="562" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success. We've just successfully queried our local MySQL database through Strapi's GraphQL playground.&lt;/p&gt;

&lt;p&gt;Great job. 🎈&lt;/p&gt;

&lt;h2&gt;
  
  
  Still to Come...
&lt;/h2&gt;

&lt;p&gt;Next time, we'll be deploying and confiuring Strapi on our Digital Ocean droplet, and we'll use the Dokku Plugin for LetsEncrypt to obtain a free certificate for our domain &lt;code&gt;vicstech.xyz&lt;/code&gt; as well as our new strapi-backend subdomain we'll be creating.&lt;/p&gt;

&lt;p&gt;Then, we'll practice querying our remote database with GraphQL, and start setting up an 11ty front-end to retrieve entries from the database .&lt;/p&gt;

&lt;p&gt;Hope to see you soon.&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>strapi</category>
      <category>dokku</category>
      <category>devops</category>
    </item>
    <item>
      <title>Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 3</title>
      <dc:creator>Victor Feight</dc:creator>
      <pubDate>Tue, 07 Jun 2022 11:04:20 +0000</pubDate>
      <link>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-3-2pg8</link>
      <guid>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-3-2pg8</guid>
      <description>&lt;p&gt;!!! warning&lt;br&gt;
    &lt;em&gt;here be dragons&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 3&lt;/li&gt;
&lt;li&gt;Apps needed to proceed&lt;/li&gt;
&lt;li&gt;Download and Install MySQL services&lt;/li&gt;
&lt;li&gt;Configure Local MySQL services and choose Authentication Method&lt;/li&gt;
&lt;li&gt;Install and configure Dokku mysql plugin (Remote)&lt;/li&gt;
&lt;li&gt;
(Remote) Playing around with our new MySQL database

&lt;ul&gt;
&lt;li&gt;Sidequest? Adding a MySQL User for extra security&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Still to come...&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 3
&lt;/h3&gt;

&lt;p&gt;Hello, welcome back! In the last post I made, I added SSH keys to my Dokku DO droplet, assigned it a floating IP, and added an A record in Cloudflare DNS settings so we can SSH into it any time with &lt;code&gt;ssh vic@apps.vicstech.xyz&lt;/code&gt;. I then set up a static IP on my host PC, installed Fail2Ban on my remote Digital Ocean Dokku droplet, and whitelisted our host PC's IP on the remote server in both SSH and Fail2Ban's configuration. In addition, I created a new user to work under and disabled root login for extra security, and hardened both Fail2Ban and SSH configurations against attackers. See &lt;a href="https://victorfeight.com/2022/03/secure_dokku_droplet/"&gt;my previous post&lt;/a&gt; for more info.&lt;/p&gt;

&lt;p&gt;For this post, I'd like to show you how to install the MySQL plugin for Dokku and see if we can test it out using MySQL workbench remotely.&lt;/p&gt;
&lt;h3&gt;
  
  
  Apps needed to proceed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v16.13.2. You can use &lt;a href="https://github.com/nvm-sh/nvm"&gt;nvm&lt;/a&gt;(node version manager) to install older versions of Node.js or manage multiple versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7-le3ovw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503154729.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7-le3ovw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503154729.png" alt="placeholder" width="443" height="107"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm (node package manager), which comes with Node.js. I'm using Git Bash in this tutorial to run npm but you could also use Powershell.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.mysql.com/downloads/mysql/"&gt;MySQL Community Server v8.0.x.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Download and Install MySQL services
&lt;/h3&gt;

&lt;p&gt;As I've already installed and set up MySQL, I'll go ahead and relaunch the MySQL Installer now, but these instructions would work for a new installation:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mUCSmKhX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503154452.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mUCSmKhX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503154452.png" alt="placeholder" width="353" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;!!! note&lt;br&gt;
    If you're installing MySQL, choose &lt;strong&gt;Developer Default&lt;/strong&gt; option to install necessary services for Strapi. Let the installer install any missing requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l5RHN01---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503155540.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l5RHN01---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503155540.png" alt="placeholder" width="758" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you get a message like the following, go ahead and click next; you can install missing requirements later.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--77UcOeel--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503155813.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--77UcOeel--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503155813.png" alt="placeholder" width="880" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let the installer continue and install any missing products.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure Local MySQL services and choose Authentication Method
&lt;/h3&gt;

&lt;p&gt;MySQL, which Strapi uses, has a client-to-server architecture, which interacts over a network. To configure the server, we need to do a MySQL &lt;strong&gt;Product Configuration&lt;/strong&gt; for &lt;strong&gt;MySQL Server&lt;/strong&gt; and &lt;strong&gt;MySQL Router&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;MySQL Server will grant the Strapi application access to the DBMS. The DBMS then handles all queries and connections.&lt;/p&gt;

&lt;p&gt;MySQL Router acts as a middleware, routing between our Strapi application and the MySQL Server.&lt;/p&gt;

&lt;p&gt;For the MySQL Server configuration type, choose "Development Computer", ensure your settings look like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5h8XRRMT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503160924.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5h8XRRMT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503160924.png" alt="placeholder" width="786" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;!!! attention&lt;br&gt;
    &lt;strong&gt;Important note:&lt;/strong&gt; We need to set the authentication method to &lt;strong&gt;Use Legacy Authentication Method&lt;/strong&gt; as I've had issues getting dokku's mysql plugin to work with Strapi using the default recommended authentication method.&lt;/p&gt;

&lt;p&gt;Ensure your screen is like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4NvJnvt5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503160958.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4NvJnvt5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503160958.png" alt="placeholder" width="786" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter a memorable password for our local MySQL root user, then click next. We want to ensure a successful connection as root user.&lt;/p&gt;

&lt;p&gt;For Windows Service settings, leave everything default:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gbUZFccO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503162229.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gbUZFccO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503162229.png" alt="placeholder" width="775" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will add the MySQL Windows Service to your system, and enable it whenever Windows restarts.&lt;/p&gt;

&lt;p&gt;Last, click &lt;strong&gt;"Next"&lt;/strong&gt; and then &lt;strong&gt;Apply Configuration&lt;/strong&gt; by clicking &lt;strong&gt;Execute&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--abKNfwP9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503162537.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--abKNfwP9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503162537.png" alt="placeholder" width="786" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Finish&lt;/strong&gt;. When prompted for Router configuration, leave it blank and continue.&lt;/p&gt;

&lt;p&gt;Finally, enter your credentials in the test scripts for &lt;strong&gt;root user&lt;/strong&gt; to execute the scripts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QiK86Fz3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503162752.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QiK86Fz3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503162752.png" alt="placeholder" width="786" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now MySQL Services and workbench should be successfully installed locally. 🌟&lt;/p&gt;
&lt;h3&gt;
  
  
  Install and configure Dokku mysql plugin (Remote)
&lt;/h3&gt;

&lt;p&gt;Now we have installed MySQL services and workbench on our &lt;em&gt;local&lt;/em&gt; Windows machine. Next, let's install &lt;a href="https://github.com/dokku/dokku-mysql"&gt;dokku-mysql&lt;/a&gt; plugin on our &lt;em&gt;remote&lt;/em&gt; Dokku instance.&lt;/p&gt;

&lt;p&gt;So far, we haven't touched a single Dokku command. Let's change that by installing the official dokku mysql plugin (currently defaults to mysql 8.0.28):&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;sudo &lt;/span&gt;dokku plugin:install https://github.com/dokku/dokku-mysql.git mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UmKbSeg2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503172505.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UmKbSeg2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503172505.png" alt="placeholder" width="880" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When it's finished installing, we can create a new database named &lt;code&gt;db&lt;/code&gt; with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku mysql:create db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2f3QTriE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503172839.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2f3QTriE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503172839.png" alt="placeholder" width="880" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the Dsn:&lt;br&gt;
&lt;code&gt;mysql://mysql:3e7b42ca09896a88@dokku-mysql-db:3306/db&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We should expose port 3307 on our remote mysql instance so that we can test a remote connection with MySQL workbench.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku mysql:expose db 3307
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;code&gt;-----&amp;gt; Service db exposed on port(s) [container-&amp;gt;host]: 3306-&amp;gt;3307&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, note the config directory: &lt;code&gt;/var/lib/dokku/services/mysql/db/config&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can cat for the root password in this directory:&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;sudo cat&lt;/span&gt; /var/lib/dokku/services/mysql/db/ROOTPASSWORD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take note of it!&lt;/p&gt;

&lt;p&gt;With the exposed port and root password, we should be able to remotely access the &lt;code&gt;db&lt;/code&gt; database from MySQL workbench.&lt;/p&gt;

&lt;p&gt;Create a new connection in MySQL Workbench home and input the following information:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hostname&lt;/strong&gt;: apps.vicstech.xyz&lt;br&gt;
&lt;strong&gt;Port&lt;/strong&gt;: 3307&lt;br&gt;
&lt;strong&gt;Username&lt;/strong&gt;: root&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Test Connection&lt;/strong&gt; and enter the mysql root password you just noted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7HEAnBL---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503175627.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7HEAnBL---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503175627.png" alt="placeholder" width="814" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success! 🌟 Some helpful tips for your new instance can be found on this &lt;a href="https://gist.github.com/JdRssll/601209434d9b7df4d384dc99bc5f392e"&gt;gist&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  (Remote) Playing around with our new MySQL database
&lt;/h3&gt;

&lt;p&gt;We can connect to our &lt;code&gt;db&lt;/code&gt; as follows but note as we're a regular user we cannot edit our database on the server: &lt;code&gt;dokku mysql:connect db&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Enter your dokku db instance with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku mysql:enter db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, while in the dokku db bash instance, enter the following, and enter the password noted earlier when prompted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7xxCcOxa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503183557.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7xxCcOxa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503183557.png" alt="placeholder" width="880" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we're logged into our remote MySQL server as root.&lt;/p&gt;

&lt;p&gt;Enter the following command at the root sql cli to see which authentication plugin our root user uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect Strapi with MySQL, we have to ensure the authentication plugin for our root user is &lt;code&gt;mysql_native_password&lt;/code&gt; as our Strapi/dokku-mysql combo currently does not support the auth plugin that comes with MySQL 8.0.x, &lt;em&gt;chaching_sha2_password&lt;/em&gt;.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zznMJ0bi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503184825.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zznMJ0bi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503184825.png" alt="placeholder" width="880" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;!!! note&lt;br&gt;
    Seeing 2 root accounts here is normal, one describes the rules for &lt;code&gt;'root'@'%'&lt;/code&gt; and the other for &lt;code&gt;'root'@'localhost'&lt;/code&gt;. I went ahead and changed both to use &lt;strong&gt;mysql_native_password.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enter the following command, replacing &lt;code&gt;password&lt;/code&gt; with a memorable root password (you can use the one from earlier, to continue testing queries in MySQL Workbench without trouble).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;mysql_native_password&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&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 sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;mysql_native_password&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;  &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then refresh privileges with this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;flush&lt;/span&gt; &lt;span class="k"&gt;privileges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resulting output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TiyjMGxx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503190840.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TiyjMGxx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/second-post-20220503190840.png" alt="placeholder" width="716" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your authentication plugin is now &lt;code&gt;mysql_native_password&lt;/code&gt;, you're good to install Strapi.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sidequest? Adding a MySQL User for extra security
&lt;/h4&gt;

&lt;p&gt;!!! note&lt;br&gt;
    MySQL also comes with some security considerations—a default superuser account called &lt;strong&gt;root&lt;/strong&gt; is enabled by default, which can dump the whole database, or create and delete any user from the user management list, and this can be a security issue. It is often sensible therefore to create another MySQL user account, with a more restricted set of privileges for daily use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Still to come...
&lt;/h3&gt;

&lt;p&gt;Next time, I'll install Strapi CMS onto Dokku and allow Strapi to talk to our MySQL instance. Last, we'll play around in GraphQL playground (built in to Strapi), querying our MySQL instance.&lt;/p&gt;

&lt;p&gt;By the end, we'll be able to use Strapi as a small scale, headless CMS blogging engine, configured with MySQL—giving our blog access to security features and user permission management.&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>devops</category>
      <category>dokku</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 2</title>
      <dc:creator>Victor Feight</dc:creator>
      <pubDate>Tue, 07 Jun 2022 10:46:20 +0000</pubDate>
      <link>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-2-59ci</link>
      <guid>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-2-59ci</guid>
      <description>&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
Table of Contents

&lt;ul&gt;
&lt;li&gt;Hardening the Ubuntu Dokku Droplet Part 1: Creating a new user in the sudo group&lt;/li&gt;
&lt;li&gt;Hardening the Ubuntu Dokku Droplet Part 2: Harden OpenSSH on Ubuntu and disable root&lt;/li&gt;
&lt;li&gt;Changing your Host to a Static IP and Implementing an IP address Allowlist Part 1&lt;/li&gt;
&lt;li&gt;Changing your Host to a Static IP and Implementing an IP address Allowlist Part 2&lt;/li&gt;
&lt;li&gt;Connecting the dots...getting a blog pushed onto Cloudflare pages, querying our MySQL database on the DO Dokku droplet via a GraphQL playground in Strapi (hosted also on our DO Dokku droplet).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Hardening the Ubuntu Dokku Droplet Part 1: Creating a new user in the sudo group
&lt;/h4&gt;

&lt;p&gt;!!! tip&lt;br&gt;
    As a preface, to anybody who's interested in hardening your Linux server, I highly recommend "Unix and Linux Administration Handbook 5th edition" which contains the following advice and more.&lt;/p&gt;

&lt;p&gt;!!! note&lt;br&gt;
    Recall, instructions prefaced with &lt;strong&gt;REMOTE&lt;/strong&gt; are run on my remote Dokku droplet in an SSH terminal.&lt;/p&gt;

&lt;p&gt;For further instructions, see &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04"&gt;here&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-new-sudo-enabled-user-on-ubuntu-20-04-quickstart"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Set the RAM swap. Although we will be temporarily increasing memory when installing Strapi, using a RAM swap decreases the chances of failure due to low memory when installing NPM packages. For more information, see &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-16-04"&gt;here&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var
&lt;span class="nb"&gt;touch &lt;/span&gt;swap.img
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 swap.img
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/swap.img &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1024k &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000
mkswap /var/swap.img
swapon /var/swap.img
free
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"/var/swap.img    none    swap    sw    0    0"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's my output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kadmYXF0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323190706.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kadmYXF0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323190706.png" alt="placeholder" width="880" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Let's adjust swappiness values and vfs_cache_pressure. swappiness values close to 100 (the default) attempt to load the SWAP with data to free up RAM. For a server, this is better closer to 0:
&lt;code&gt;sudo sysctl vm.swappiness=10&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;vfs_cache_pressure is related to inode/filesystem access and should be set conservatively, so inode information is removed from cache less often:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo sysctl vm.vfs_cache_pressure=50&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ixx87uYT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323195400.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ixx87uYT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323195400.png" alt="placeholder" width="827" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make these settings persist, &lt;code&gt;sudo nano /etc/sysctl.conf&lt;/code&gt; and add the following to the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vm.swappiness = 10
vm.vfs_cache_pressure = 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7m2FgN7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323195540.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7m2FgN7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323195540.png" alt="placeholder" width="788" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Being root user is a huge security problem, as you could wipe your system in a single command if you're not careful. Let's remedy this by creating our own user account and later we'll be turning off root account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;REMOTE&lt;/strong&gt;: Create a new user as root with &lt;code&gt;adduser &amp;lt;username&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S94FX0w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323191032.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S94FX0w5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323191032.png" alt="placeholder" width="728" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REMOTE&lt;/strong&gt;: Grant that user administrative privileges with &lt;code&gt;usermod -aG sudo &amp;lt;username&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Next, we copy Public SSH keys from the &lt;code&gt;root&lt;/code&gt; user to our own user.
The set of commands is as follows, replace &lt;code&gt;vic&lt;/code&gt; with the user you created:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/vic/.ssh &lt;span class="c"&gt;# Create ssh directory&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 /home/vic/.ssh &lt;span class="c"&gt;# Apply directory permissions&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; /root/.ssh/authorized_keys /home/vic/.ssh/authorized_keys &lt;span class="c"&gt;# Copy the ssh authorized key&lt;/span&gt;
&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; vic:vic /home/vic/.ssh &lt;span class="c"&gt;# Apply user and group permissions&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 /home/vic/.ssh/authorized_keys &lt;span class="c"&gt;# Apply file permissions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LOCAL&lt;/strong&gt;: Reconnect to the droplet as your new user. &lt;code&gt;exit&lt;/code&gt; the root remote connection, then, on a local Git Bash window, run &lt;code&gt;ssh &amp;lt;user&amp;gt;@&amp;lt;subdomain&amp;gt;.vicstech.xyz&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n_A6Z1Yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323192617.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n_A6Z1Yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323192617.png" alt="placeholder" width="802" height="986"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;REMOTE&lt;/strong&gt;: Delete the Docker ports which are opened by default to mitigate security risks.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status

&lt;span class="c"&gt;# Delete Docker UFW rules&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw delete allow 2375/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw delete allow 2376/tcp

&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;My output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f1gE_5MS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323193107.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f1gE_5MS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323193107.png" alt="placeholder" width="749" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice, we're getting there. 🌷&lt;/p&gt;

&lt;p&gt;We've set up a new user account with sudo privileges and added a swap file.&lt;/p&gt;
&lt;h4&gt;
  
  
  Hardening the Ubuntu Dokku Droplet Part 2: Harden OpenSSH on Ubuntu and disable root
&lt;/h4&gt;

&lt;p&gt;We're almost finished setting up our server. The last things we need to do are harden our security settings for OpenSSH, and install Fail2Ban to prevent unwanted logins.&lt;/p&gt;

&lt;p&gt;Information is taken from this &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-harden-openssh-on-ubuntu-18-04"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we're logged in as a new user, let's go ahead and harden OpenSSH then install Fail2Ban.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REMOTE&lt;/strong&gt;: Backup configuration file: &lt;code&gt;sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REMOTE&lt;/strong&gt;: Edit the SSHD configuration with &lt;code&gt;sudo nano /etc/ssh/sshd_config&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Change the following settings to more secure ones as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 20
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YDb2g50r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323201322.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YDb2g50r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323201322.png" alt="placeholder" width="791" height="296"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kh6J8Dmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323201600.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kh6J8Dmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323201600.png" alt="placeholder" width="834" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REMOTE&lt;/strong&gt;: Validate the syntax with &lt;code&gt;sudo sshd -t&lt;/code&gt;, which should have no output if everything is correct:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UBTQSkKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323201737.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UBTQSkKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323201737.png" alt="placeholder" width="862" height="59"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REMOTE&lt;/strong&gt;: Reload the new settings with &lt;code&gt;sudo service sshd reload&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LOCAL&lt;/strong&gt;: Now, if we attempt to SSH at root, we get the following output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hYTxH7TV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323203543.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hYTxH7TV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323203543.png" alt="placeholder" width="558" height="80"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alright, good. Now root can no longer login, which was a big security risk. We'll continue by implementing an IP Address Allowlist.&lt;/p&gt;

&lt;p&gt;The basic idea behind this is Deny By Default security, in which all inbound and outbound traffic not expressly permitted by SSHD service is blocked. 🛑&lt;/p&gt;
&lt;h4&gt;
  
  
  Changing your Host to a Static IP and Implementing an IP address Allowlist Part 1
&lt;/h4&gt;

&lt;p&gt;!!! note&lt;br&gt;
    The following commands are all local.&lt;/p&gt;

&lt;p&gt;Even if by some odd chance your private keys or password gets leaked, if you have implemented an IP address allow list, you can ensure only users on those IP addresses can login. Let's do that now.&lt;/p&gt;

&lt;p&gt;!!! tip&lt;br&gt;
    For the purposes of this guide, I'll be setting up my Ethernet IP as static, though the instructions are similar for a Wi-Fi adapter.&lt;/p&gt;

&lt;p&gt;I'll be taking steps from &lt;a href="https://www.hellotech.com/guide/for/how-to-set-static-ip-windows-10"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Type &lt;code&gt;ip address&lt;/code&gt; into the Windows 10 search bar, then click "Ethernet Settings"&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_kSxtjLy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210103.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_kSxtjLy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210103.png" alt="placeholder" width="421" height="172"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click "Change Adapter options"&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1LHPJuF8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210216.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1LHPJuF8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210216.png" alt="placeholder" width="768" height="332"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Right click on Ethernet to set up a static IP over Ethernet, or right click on WiFi if your computer is connected via WiFi --&amp;gt; Then click "Status"&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y5_fXmRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210348.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y5_fXmRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210348.png" alt="placeholder" width="441" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click "Details"&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bB0finPn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210436.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bB0finPn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210436.png" alt="placeholder" width="362" height="253"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Take note of the following info: IPv4 address, IPv4 subnet mask, IPv4 default gateway, and IPv4 DNS server.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;!!! note&lt;br&gt;
    As my IP is currently Static, it says DHCP is &lt;em&gt;not&lt;/em&gt; enabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A307AZX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210952.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A307AZX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323210952.png" alt="placeholder" width="359" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This information can also be found via &lt;code&gt;ip config /all&lt;/code&gt; on the Windows CMD prompt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cu0IYTxd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323212014.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cu0IYTxd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323212014.png" alt="placeholder" width="768" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: the adapter's name is "Ethernet"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IPv4 Address: 10.0.0.220 (Preferred)&lt;/li&gt;
&lt;li&gt;IPv4 Subnet Mask: 255.255.255.0&lt;/li&gt;
&lt;li&gt;IPv4 Default Gateway: 10.0.0.1&lt;/li&gt;
&lt;li&gt;IPv4 DNS Servers:

&lt;ul&gt;
&lt;li&gt;75.75.75.75&lt;/li&gt;
&lt;li&gt;75.75.76.76&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;We can set our static IP address graphically or through the CMD. To set it graphically, follow the rest of &lt;a href="https://www.hellotech.com/guide/for/how-to-set-static-ip-windows-10"&gt;this guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To set it manually via CMD using &lt;code&gt;netsh&lt;/code&gt; command, keep following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To set a static IP address to 10.0.0.220 for an adapter named "Ethernet", with subnet mask 255.255.255.0, and Gateway 10.0.0.1:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   IPv4 Address. . . . . . . . . . . : 10.0.0.180(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Lease Obtained. . . . . . . . . . : Thursday, February 24, 2022 9:07:06 AM
   Lease Expires . . . . . . . . . . : Saturday, February 26, 2022 9:07:06 AM
   Default Gateway . . . . . . . . . : fe80::10:18ff:fe46:236a%12
   10.0.0.1
   DHCP Server . . . . . . . . . . . : 10.0.0.1

C:\WINDOWS\system32&amp;gt;netsh interface ip set address name="Ethernet" static 10.0.0.220 255.255.255.0 10.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output of &lt;code&gt;ipconfig /all&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OfuYPa8L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323213628.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OfuYPa8L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323213628.png" alt="placeholder" width="786" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you're using a Wifi Adapter, check the name in &lt;code&gt;ipconfig /all&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EjEOe3Uw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323212125.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EjEOe3Uw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323212125.png" alt="placeholder" width="677" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your adapter name would be "Wi-Fi" (case-sensitive).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To set the DNS servers:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set DNS servers:
C:\WINDOWS\system32&amp;gt;netsh interface ip set dns name="Ethernet" static 75.75.75.75
C:\WINDOWS\system32&amp;gt;netsh interface ip add dns name="Ethernet" 75.75.76.76 index=2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output of &lt;code&gt;ipconfig /all&lt;/code&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wr1qe65M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323213733.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wr1qe65M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323213733.png" alt="placeholder" width="630" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, that's it. We're static. ⚡&lt;br&gt;
There's a lot of other benefits to setting your static IP such as ease of SSH use and port forwarding, but I won't get into that here.&lt;/p&gt;

&lt;p&gt;For our next steps, let's finish configuring OpenSSH on our remote server, then configure Fail2Ban.&lt;/p&gt;
&lt;h4&gt;
  
  
  Changing your Host to a Static IP and Implementing an IP address Allowlist Part 2
&lt;/h4&gt;

&lt;p&gt;!!! note&lt;br&gt;
    These instructions are taken from the latter half of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-harden-openssh-on-ubuntu-18-04"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we're going to implement an IP Address Allowlist in both OpenSSH and Fail2Ban.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LOCAL&lt;/strong&gt;: If you're not already, log on to the remote server again with &lt;code&gt;ssh vic@apps.vicstech.xyz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: press &lt;code&gt;w&lt;/code&gt; and enter to see your connecting IP:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0uatXr5I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323215013.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0uatXr5I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323215013.png" alt="placeholder" width="692" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;!!! note&lt;br&gt;
    My remote Dokku server's connecting IP is &lt;strong&gt;71.59.236.28&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Edit our OpenSSH server configuration: &lt;code&gt;sudo nano /etc/ssh/sshd_config&lt;/code&gt; and add some configuration directives:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AllowUsers *@71.59.236.28 *@71.59.236.0/27
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here we restrict all users to a specific IP address and a range.&lt;/p&gt;

&lt;p&gt;To be a bit more secure, you can remove the range if you want.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Run &lt;code&gt;sudo service sshd reload&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Congratulations, your SSHD server is hardened. Next, let's install Fail2Ban to prevent any unwanted users from accessing our server.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Let's update with &lt;code&gt;sudo apt update&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DO1j6cfh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323233139.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DO1j6cfh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323233139.png" alt="placeholder" width="786" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Install Fail2Ban on our Dokku server with &lt;code&gt;sudo apt install fail2ban&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XRiZtuYG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323233351.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XRiZtuYG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323233351.png" alt="placeholder" width="843" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REMOTE&lt;/strong&gt;: Create a .local Fail2Ban configuration file from the default jail.conf: &lt;code&gt;sudo cp /etc/fail2ban/jail.{conf,local}&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REMOTE:&lt;/strong&gt; Open the jail.conf file with &lt;code&gt;sudo nano /etc/fail2ban/jail.local&lt;/code&gt; and uncomment the line starting with ignoreip, add our local IP address followed by all machines to whitelist (insert your IP from earlier here):&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;ignoreip = 127.0.0.1/8 ::1 71.59.236.28 71.59.236.0/27&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Output from &lt;code&gt;/etc/fail2ban/jail.local&lt;/code&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HrU330tq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323234007.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HrU330tq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323234007.png" alt="placeholder" width="855" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Modify the following additional settings in &lt;code&gt;/etc/fail2ban/jail.local&lt;/code&gt;:&lt;br&gt;
For more information on these options and more, see &lt;a href="https://linuxize.com/post/install-configure-fail2ban-on-ubuntu-20-04/"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;!!! note&lt;br&gt;
    Set 1d to a negative like -99d if you want to permaban them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bantime  = 1d
maxretry = 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REMOTE&lt;/strong&gt;: Finally, restart Fail2Ban to apply the configurations -- &lt;code&gt;sudo systemctl restart fail2ban&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So we've set up a static IP on our host PC, installed Fail2Ban on our remote Dokku server, and whitelisted our host PC's IP on the remote server in both SSHD and Fail2Ban configurations. We've also disabled root login and hardened both configurations against attackers.&lt;/p&gt;

&lt;p&gt;It's a lot... but it's necessary in this dog eat dog world. 🤷🏼‍♂️&lt;br&gt;
Check out your Fail2Ban logs sometime, you might see some surprising brute force and automated attack attempts...&lt;/p&gt;

&lt;p&gt;Soon, finally, we will get to the cooler stuff 🥁&lt;/p&gt;

&lt;p&gt;Setting up an Eleventy blog with MySQL-hosted posts and a Strapi CMS on our shiny new and secure server. 💻 🕷&lt;/p&gt;

&lt;h4&gt;
  
  
  Connecting the dots...getting a blog pushed onto Cloudflare pages, querying our MySQL database on the DO Dokku droplet via a GraphQL playground in Strapi (hosted also on our DO Dokku droplet).
&lt;/h4&gt;

&lt;p&gt;I'll save the nitty-gritty, inner-workings of my blog and how I emulate &lt;a href="https://github.com/google/eleventy-high-performance-blog"&gt;Eleventy's high performance&lt;/a&gt; blog example for later. For now, we just want to connect the dots...let's see what we have and what we still need to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install MySQL plugin for Dokku&lt;/li&gt;
&lt;li&gt;Install Strapi CMS onto Dokku&lt;/li&gt;
&lt;li&gt;Allow Strapi CMS to talk to our MySQL instance.&lt;/li&gt;
&lt;li&gt;Query from our MySQL instance using GraphQL query playground (built-in to Strapi).&lt;/li&gt;
&lt;li&gt;Pull queries into Blog markdown files and present them with Nunjucks templating language (and some JS) in Eleventy.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mysql</category>
      <category>digitalocean</category>
      <category>cloudflare</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 1</title>
      <dc:creator>Victor Feight</dc:creator>
      <pubDate>Tue, 07 Jun 2022 09:03:52 +0000</pubDate>
      <link>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-1-2c63</link>
      <guid>https://dev.to/victorfeight/deploy-an-eleventy-blog-on-cloudflare-pages-with-strapi-mysql-and-dokku-on-a-digital-ocean-droplet-part-1-2c63</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 1&lt;/li&gt;
&lt;li&gt;Getting a Domain Name from Namesilo.com&lt;/li&gt;
&lt;li&gt;Creating SSH Keys and adding them to your Digital Ocean Account&lt;/li&gt;
&lt;li&gt;Creating a new Digital Ocean Dokku droplet to hold our apps and MySQL database&lt;/li&gt;
&lt;li&gt;Getting free fast static site hosting and DNS management from Cloudflare Pages and adding new A records&lt;/li&gt;
&lt;li&gt;Setting our nameservers to Cloudflare's within Namesilo Domain Manager&lt;/li&gt;
&lt;li&gt;SSH into the Dokku droplet from Windows 10&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 1
&lt;/h2&gt;

&lt;p&gt;For my first article, I'll review all the steps required to setting up a new digital ocean Dokku Ubuntu droplet and hardening it. Throughout this process you will be picking up a few DevOps and Front-end skills to add to your toolset. By the end, you'll have your very own secured remote Dokku server to host your static site, apps, and database. Next time I'll cover installing &lt;a href="https://github.com/dokku/dokku-mysql"&gt;Dokku mysql&lt;/a&gt; and a Strapi CMS on our new Dokku server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting a Domain Name from Namesilo.com
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;For the first step, you'll be deciding if you want a domain name. It doesn't really matter who you choose to go with, I personally go with &lt;a href="https://www.namesilo.com/"&gt;Namesilo&lt;/a&gt; because they're cheap, easy, and they don't have "daddy" in the name. 😏&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the purposes of this tutorial, I'll walk you through creating a new domain name with namesilo.com, though do note that Cloudflare also sells domain names if you want to do it all through them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;After creating an account at namesilo.com, search a new domain name to create. For this tutorial, I'll be using vicstech.xyz (since it's only 99 cents). Press the Add button, then "Checkout".&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4cBpzekf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323142841.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4cBpzekf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323142841.png" alt="placeholder" width="880" height="363"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After purchasing, leave "Service link" as "None (default)". We'll be configuring our DNS nameservers we get from Cloudflare pages in a bit, so leave that blank for now, so go ahead and click "Skip this step".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click domain manager, then the little 🌎 icon next to our new domain name to get to DNS management.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w3-YFhlo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323143928.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w3-YFhlo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323143928.png" alt="placeholder" width="880" height="399"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YtJMxRxP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323143830.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YtJMxRxP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323143830.png" alt="placeholder" width="880" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cool, now I have a new domain name, vicstech.xyz. By default, it creates a few A records and CNAME records for default hostname mapping settings. As we will be configuring our DNS through Cloudflare pages, feel free to leave them or delete them. Leave this tab open however, we'll be coming back to it in later.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rSFEy8Hv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323143645.png" alt="placeholder" width="880" height="640"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, we'll be creating an SSH key pair to securely login to our new SSH Dokku droplet once it's up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating SSH Keys and adding them to your Digital Ocean Account
&lt;/h2&gt;

&lt;p&gt;I'll walk you through the process of creating the SSH keys on Windows that we'll need for our Dokku container later. Note the process is similar on Linux, full directions for DO are &lt;a href="https://docs.digitalocean.com/products/droplets/how-to/add-ssh-keys/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If you could kindly sign up through my &lt;a href="https://m.do.co/c/89cb0ce13d46"&gt;Digital Ocean Referral&lt;/a&gt;, you'll get a free $100 to start which is plenty to play around.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install &lt;a href="https://gitforwindows.org/"&gt;Git for Windows&lt;/a&gt;, go through with all the default options.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After installing, open a new Git Bash window, and run the following command to generate a 2048-bit RSA key pair, stored in ~/.ssh (which, on Windows 10, is &lt;code&gt;C:\Users\Vic\.ssh&lt;/code&gt; for me):&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;After signing up and creating a new account on Digital Ocean, we'll upload the contents of &lt;code&gt;id_rsa.pub&lt;/code&gt; to Digital Ocean. Login to your &lt;a href="https://cloud.digitalocean.com/"&gt;control panel&lt;/a&gt; and click "Settings" on the side.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B1BU6JR0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323153916.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B1BU6JR0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323153916.png" alt="placeholder" width="862" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click "Security" tab and then "Add SSH Key" button near the top.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hOuVhD4Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323154026.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hOuVhD4Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323154026.png" alt="placeholder" width="880" height="182"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy and paste the contents of id_rsa.pub into the "SSH key content" field on the New SSH Key dialog. Name it "SSH_DESKTOP" and click "Add SSH Key". Cool, now we've got an SSH key readily available in DO for any of our new droplets. 😎&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gZZAqLj_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323154129.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gZZAqLj_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323154129.png" alt="placeholder" width="880" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we'll be creating a new Digital Ocean Dokku droplet and add our generated SSH key for use in our Dokku applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a new Digital Ocean Dokku droplet to hold our apps and MySQL database
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;After signing up and creating a new account, let's go ahead and create a new Dokku droplet. After creating a new Project, let's set up a Dokku droplet found &lt;a href="https://marketplace.digitalocean.com/apps/dokku?utm_medium=marketplace&amp;amp;utm_source=marketplace&amp;amp;utm_campaign=marketplace&amp;amp;utm_content=no_image"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qOfyF82D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323151305.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qOfyF82D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323151305.png" alt="placeholder" width="880" height="322"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go with whatever defaults you prefer. I chose Regular Intel with SSD plan, 1GB CPU / 25GB SSD ($5/mo plan). I did not add block storage, and I chose San Francisco data center as that is closest to me.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Uf5pOzrW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323155000.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Uf5pOzrW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323155000.png" alt="placeholder" width="880" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;!!! note&lt;br&gt;
    We are going to need to up this to around 4GB later temporarily, a requirement to get Strapi installed correctly. Luckily, Digital Ocean allows you add and remove CPU/RAM quite easily.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Before finishing up, ensure you tick the checkbox for the SSH Keys you created earlier. Finally, click "Create Droplet" button at the bottom.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QQCOanDP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323161356.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QQCOanDP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323161356.png" alt="placeholder" width="767" height="234"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After it's created, let's visit the droplet. Click the Droplets tab on the left, then click on the name of the newly created dokku droplet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, go to the Networking tab of your new droplet and assign a Floating IP. We'll need this to tie to our new domain name later.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qeeW1uIy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323161824.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qeeW1uIy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323161824.png" alt="placeholder" width="880" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It should look like this after assigning a Floating IP:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UIqbX7Gy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323162050.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UIqbX7Gy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323162050.png" alt="placeholder" width="880" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Visit your new Floating IP in the browser to find the Dokku setup screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you had ticked the SSH Keys box this should be filled, otherwise go ahead and copy and paste from your local id_rsa.pub file (generated by ssh-keygen). For hostname, I chose my subdomain as "apps" following by &lt;code&gt;.vicstech.xyz&lt;/code&gt;, the domain I just created with namesilo.&lt;/p&gt;

&lt;p&gt;Be sure to check "Use virtualhost naming for apps" as it makes naming conventions easier, and finally click "Finish setup".&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GAfr4ZGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323162539.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GAfr4ZGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323162539.png" alt="placeholder" width="627" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's review what we've got: A domain name tied to a floating IP (accessible from anywhere), and a Dokku droplet configured with our SSH key for authentication. This will allow us to SSH into the Dokku server as the "dokku" user later on.&lt;/p&gt;

&lt;p&gt;Next, we can configure Cloudflare to use our Dokku droplet's newly assigned floating IP address.&lt;/p&gt;

&lt;p&gt;Open up a new tab at &lt;a href="https://pages.cloudflare.com/"&gt;Cloudflare Pages&lt;/a&gt;. We will be setting up a new account to host our static websites and manage our DNS through cloudflare's blazing fast DNS services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting free fast static site hosting and DNS management from Cloudflare Pages and adding new A records
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Sign up for a free account at &lt;a href="https://pages.cloudflare.com/"&gt;Cloudflare Pages&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter your new site's domain name and click "Add site" button (note you can also register a new domain through Cloudflare directly, if you so choose).&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xlYDCfA7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323144510.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xlYDCfA7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323144510.png" alt="placeholder" width="880" height="204"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go ahead and click the Free plan and click the "Continue" button.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ERerc26n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323144648.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ERerc26n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323144648.png" alt="placeholder" width="880" height="260"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As we'll be letting cloudflare manage our DNS, let's set up some DNS records. Click "Add record", for type choose "A", for name the chosen subdomain which is "apps" in my case, for IPv4 put the floating IP from the Dokku droplet. Untick the Cloudflare Proxy, for DNS only mode, as Proxy mode can cause problems when connecting.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X2Ztt097--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323164331.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X2Ztt097--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323164331.png" alt="placeholder" width="825" height="563"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will be for our main subdomain. This will allow me to SSH into "apps.vicstech.xyz" soon enough.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a "catch-all" rule, using &lt;code&gt;*.apps&lt;/code&gt; as the name, so that anything deployed on the subdomain will be forwarded to the droplet. Be sure to also disable the Cloudflare proxy, then click "Continue" for now.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d6EQa5wE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323165005.png" alt="placeholder" width="644" height="76"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before we go too much further, we're going to set the nameservers to our Cloudflare ones within Namesilo Domain manager settings.&lt;/p&gt;

&lt;p&gt;Cloudflare should instruct us to do this the first time through:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jT-n2cTt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323170329.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jT-n2cTt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323170329.png" alt="placeholder" width="817" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll do exactly that next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting our nameservers to Cloudflare's within Namesilo Domain Manager
&lt;/h2&gt;

&lt;p&gt;!!! note&lt;br&gt;
    Now before we forget, let's ensure Cloudflare is actually the one managing our DNS by setting the nameservers in Namesilo domain manager to our Cloudflare DNS nameservers&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Note the Cloudflare Nameservers: dell.ns.cloudflare.com, and zeus.ns.cloudflare.com.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q-6riTRh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323165952.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q-6riTRh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323165952.png" alt="placeholder" width="880" height="236"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From Domain Manager in Namesilo, tick the domain to update, and click "Change Nameservers".&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F-eOX7Ed--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323170600.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F-eOX7Ed--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323170600.png" alt="placeholder" width="880" height="331"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete the default nameservers and add Cloudflare's given ones, then click "Submit".&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bzEPJzgl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323170919.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bzEPJzgl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323170919.png" alt="placeholder" width="672" height="317"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Within Cloudflare, click "Done, check nameservers" and go through some configuration recommendations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Apply both the options:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ggz6cgK9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323171305.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ggz6cgK9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323171305.png" alt="placeholder" width="880" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few minutes, you should get a success message upon reloading:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MwNRhBF5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323173341.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MwNRhBF5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323173341.png" alt="placeholder" width="880" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool. Now our new vicstech.xyz domain is being managed by Cloudflare. 🧐 Ultimately, we want our static website to be reachable on our main domain, vicstech.xyz, to be able to SSH to our apps subdomain, apps.vicstech.xyz, and all of our serverside applications reachable by further subdomains, such as strapi.apps.vicstech.xyz.&lt;/p&gt;

&lt;p&gt;Later on, when we have an actual Eleventy blog to push, we'll add some additional records to our Cloudflare DNS settings, so that &lt;a href="http://www.vicstech.xyz"&gt;www.vicstech.xyz&lt;/a&gt; will redirect to vicstech.xyz and vicstech.xyz will redirect to the url produced by Cloudflare page build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.cloudflare.com/t/redirect-www-example-com-to-example-com/78347"&gt;https://community.cloudflare.com/t/redirect-www-example-com-to-example-com/78347&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can finally SSH into the new Dokku Droplet. Pull up the hacker shades.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSH into the Dokku droplet from Windows 10
&lt;/h2&gt;

&lt;p&gt;Alright, so we have got our domain name set up and configured behind Cloudflare pages, we have configured Namesilo to use Cloudflare's nameservers, we've added appropriate A records for redirection rules to our subdomain apps, we have enabled HTTPS and Autominify services from Cloudflare, and we have set up a new Dokku droplet (configured with our SSH keys). Feel free to grab some tea, this next part gets pretty Linuxy. 🍵&lt;/p&gt;

&lt;p&gt;!!! info&lt;br&gt;
    I will be prefacing each instruction with either &lt;strong&gt;LOCAL&lt;/strong&gt; or &lt;strong&gt;REMOTE&lt;/strong&gt; meaning "Launch these commands from your LOCAL computer" and "Launch these commands from the REMOTE DOKKU DROPLET", respectively.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LOCAL&lt;/strong&gt;: Launch Git bash. First, add our Private SSH Key to our local terminal so we can authenticate and connect to our droplet.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;ssh-agent &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="c"&gt;# Start the agent that holds on to our keys&lt;/span&gt;
ssh-add &lt;span class="s1"&gt;'~/path/to/ssh/private.key'&lt;/span&gt; &lt;span class="c"&gt;# Add our private SSH key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, it looks like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mWH7-hJo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323183754.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mWH7-hJo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323183754.png" alt="placeholder" width="693" height="162"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ty11mlag--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323183732.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ty11mlag--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323183732.png" alt="placeholder" width="649" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LOCAL&lt;/strong&gt;: Check the key was added correctly with &lt;code&gt;ssh-add -l&lt;/code&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4oFNadx9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323183853.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4oFNadx9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323183853.png" alt="placeholder" width="874" height="94"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LOCAL&lt;/strong&gt;: Finally, let's try to SSH into the Dokku droplet.&lt;br&gt;
To enter your new server, type: &lt;code&gt;ssh root@&amp;lt;subdomain name&amp;gt;&lt;/code&gt; ie. &lt;code&gt;ssh root@apps.vicstech.xyz&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hopefully, you'll get output similar to the following:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yPvHcj6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323184228.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yPvHcj6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://victorfeight.com/img/Pasted-image-20220323184228.png" alt="placeholder" width="880" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm in. 👨🏻‍💻&lt;/p&gt;

&lt;p&gt;Pretty soon we'll add a Strapi node application to our fresh server, that will be reachable at &lt;code&gt;strapi.vicstech.xyz&lt;/code&gt;, containerized and speaking to a MySQL service on the same host. But before that, we've got to do some administration tasks on our new server.&lt;/p&gt;

&lt;p&gt;Now, take a breather, because in my next article we'll be hardening up our server. Digital Ocean gives us pretty much our own server and with that much power comes great responsibility. 🕷&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloudflare</category>
      <category>mysql</category>
    </item>
  </channel>
</rss>
