<?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>Forem: Aaron Ellington</title>
    <description>The latest articles on Forem by Aaron Ellington (@aaronellington).</description>
    <link>https://forem.com/aaronellington</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%2F68263%2F550447ef-eef7-4c27-825d-f2c4d7862a31.png</url>
      <title>Forem: Aaron Ellington</title>
      <link>https://forem.com/aaronellington</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aaronellington"/>
    <language>en</language>
    <item>
      <title>My Desk Setup</title>
      <dc:creator>Aaron Ellington</dc:creator>
      <pubDate>Tue, 09 May 2023 12:00:00 +0000</pubDate>
      <link>https://forem.com/aaronellington/my-desk-setup-4lp8</link>
      <guid>https://forem.com/aaronellington/my-desk-setup-4lp8</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A-YEi4Sv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/911108D.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A-YEi4Sv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/911108D.jpeg" alt="Final Desk Setup" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting Items

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://a.co/d/i0ha0Cs"&gt;Motorized Standing Desk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a.co/d/g8GsfRC"&gt;Mesh Net for Cable Managment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a.co/d/5SP4oay"&gt;Under-Desk Headphone Hook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a.co/d/1zf20C2"&gt;Desk Wheels&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Devices/Webcam Setup

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.apple.com/macbook-air-m2/"&gt;MacBook Air M2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a.co/d/bPkdedZ"&gt;Dell S2722QC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apple.com/airpods-max/"&gt;AirPods Max&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a.co/d/bxlct1u"&gt;Tripod Mount MagSafe Stand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thingiverse.com/thing:6017032"&gt;Dell S2722QC Tripod-like mount&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apple.com/iphone-14-pro/"&gt;iPhone 14 Pro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Extra Items

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://a.co/d/7PiraYa"&gt;MagSafe iPhone/AirPod Charger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xbox.com/en-US/consoles/xbox-series-s"&gt;Xbox Series S&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thingiverse.com/thing:5964838"&gt;Xbox Controller Stand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a.co/d/7xrnQyq"&gt;Water Bottle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Step 1: Define Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Quickly switch between standing and sitting&lt;/li&gt;
&lt;li&gt;Easily move around the house&lt;/li&gt;
&lt;li&gt;Clutter free (as much as possible)&lt;/li&gt;
&lt;li&gt;One computer to use at home and on the go&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Computer and Desk
&lt;/h2&gt;

&lt;p&gt;First, I need a computer. With my requirement of using the same computer on the go and at home I need a laptop. Any laptop would due as long as it supports charging and an external monitor via a single USB-C cable. I choose a &lt;a href="https://www.apple.com/macbook-air-m2/"&gt;MacBook Air M2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next is the desk. I just got a sturdy and cheap &lt;a href="https://a.co/d/i0ha0Cs"&gt;Motorized Standing Desk&lt;/a&gt; on Amazon.&lt;/p&gt;

&lt;p&gt;I didn't want a bunch of cables hanging off the back of my desk and I didn't want anything too complicated so I got a &lt;a href="https://a.co/d/g8GsfRC"&gt;Mesh Net&lt;/a&gt; for cable management. It made it easy to just shove a bunch of wires in there and not worry about it.&lt;/p&gt;

&lt;p&gt;I wanted to be able to roll my desk around so I got some &lt;a href="https://a.co/d/1zf20C2"&gt;Wheels&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also wanted to have a nice spot to hang my &lt;a href="https://www.apple.com/airpods-max/"&gt;AirPods Max&lt;/a&gt; headset so I ordered a &lt;a href="https://a.co/d/5SP4oay"&gt;Under-Desk Headphone Hook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've actually already achieved all of my requirements, but I think I can do better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OeIomOOR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/vYjYu4h.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OeIomOOR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/vYjYu4h.jpeg" alt="Desk with laptop" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vbnjcCHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/HXa40uP.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vbnjcCHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/HXa40uP.jpg" alt="Under desk" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Bigger Screen
&lt;/h2&gt;

&lt;p&gt;While I can do all of my work on a 13-inch laptop... I would like a bigger screen.&lt;/p&gt;

&lt;p&gt;I wanted a 4K monitor but I wanted to continue to plug just 1 wire into my laptop when I sat down at my desk. I found some good USB-C docks but I was dreading the cost as well as the extra wire clutter it would cause.&lt;/p&gt;

&lt;p&gt;But then I found the &lt;a href="https://a.co/d/bPkdedZ"&gt;Dell S2722QC&lt;/a&gt;, a 27-inch 4K USB-C monitor that will both charge my laptop and work as a second display with a SINGLE cable. Perfect!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s9gy9FBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/bK7IYuj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s9gy9FBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/bK7IYuj.jpg" alt="With Monitor" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Webcam
&lt;/h2&gt;

&lt;p&gt;Consumer webcams suck. Since I was using macOS I was able to use my iPhone as a webcam through the "Camera Continuity" feature. I needed a good way to mount my phone above my monitor but I could't find any great options online.&lt;/p&gt;

&lt;p&gt;For convenience, I wanted to magnetically attach my phone. So I designed and 3D printed &lt;a href="https://www.thingiverse.com/thing:6017032"&gt;this mount&lt;/a&gt; for a &lt;a href="https://a.co/d/bxlct1u"&gt;Tripod Mount MagSafe Stand&lt;/a&gt; that I found. It fit perfectly on the back of my new monitor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xrqem7eC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/XibyfnQ.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xrqem7eC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/XibyfnQ.jpg" alt="3D printed monitor mount" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i5vXrMvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/aXEPc0k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i5vXrMvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/aXEPc0k.jpg" alt="3D printed monitor mount" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RTWAW08A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/EvjGjnD.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RTWAW08A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/EvjGjnD.jpg" alt="3D printed monitor mount" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Mostly Final Setup
&lt;/h2&gt;

&lt;p&gt;This is my final-ish setup, realistically my desk is not going to be this clean looking. I'm missing a few extras.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O1qmLTJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/ItWcwCk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O1qmLTJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/ItWcwCk.jpg" alt="Final Product" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Actual Final Setup
&lt;/h2&gt;

&lt;p&gt;I added a wire to charge my headset and a &lt;a href="https://a.co/d/7PiraYa"&gt;MagSafe iPhone/AirPod Charger&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I had this nice 4k monitor that I didn't want to go to waste, so I hooked up my &lt;a href="https://www.xbox.com/en-US/consoles/xbox-series-s"&gt;Xbox Series S&lt;/a&gt; on my desk and to my monitor. Now I can use my monitor as my an extended display for my laptop or the console.&lt;/p&gt;

&lt;p&gt;Underneath the desk still looks pretty clean and I still just have a single power cable going out the back of my desk.&lt;/p&gt;

&lt;p&gt;Mission accomplished.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w0myCqL7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/rfEAqBq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w0myCqL7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/rfEAqBq.jpg" alt="s" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ps-vLOSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/NKJr2gX.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ps-vLOSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/NKJr2gX.jpg" alt="s" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>remotelife</category>
    </item>
    <item>
      <title>5 Git Tips and Tricks</title>
      <dc:creator>Aaron Ellington</dc:creator>
      <pubDate>Mon, 20 Jan 2020 04:26:10 +0000</pubDate>
      <link>https://forem.com/aaronellington/5-git-tips-and-tricks-40bd</link>
      <guid>https://forem.com/aaronellington/5-git-tips-and-tricks-40bd</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fdYQZeTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1556075798-4825dfaaf498%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fdYQZeTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1556075798-4825dfaaf498%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="5 Git Tips and Tricks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some of my favorite git tips and tricks that I either recently learned or that I use all the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.&lt;code&gt;.git/safe/../../bin&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the article that I learned this trick from and it explains it better than I could: &lt;a href="https://thoughtbot.com/blog/git-safe"&gt;thoughtbot - git-safe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the biggest benefits that see from this is using it to override what version of a command is used while working on the project.&lt;/p&gt;

&lt;p&gt;Example: You have &lt;code&gt;phpunit&lt;/code&gt; installed globally on your system but the project needs to use a specific version. Just add the specific version to the bin directory of your project, run &lt;code&gt;mkdir .git/bin&lt;/code&gt;, and that's it! As long as you add &lt;code&gt;.git/safe/../../bin&lt;/code&gt; to the beginning of your &lt;code&gt;$PATH&lt;/code&gt; it will override other entries.&lt;/p&gt;

&lt;p&gt;Another thing to note here is you can use it on other directories besides &lt;code&gt;bin/&lt;/code&gt;. You could add something like &lt;code&gt;.git/safe/../../vendor/.bin/&lt;/code&gt; as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Aliases
&lt;/h2&gt;

&lt;p&gt;While they are not much different than a shell alias, I do like git aliases more because they are nicely organized under the &lt;code&gt;~/.gitconfig&lt;/code&gt; file. Here are a few that I use on a daily basis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[alias]
    newbranch = "!f() { git checkout -b $1 master &amp;amp;&amp;amp; git push -u origin $1 ; }; f"
    acp = "!f() { git add --all &amp;amp;&amp;amp; git commit -m \"$1\" &amp;amp;&amp;amp; git push ; } ; f"
    wip = "!git acp 'wip'"
    undo = "!git reset HEAD^"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I try not to go crazy with them since I can not rely on them always being there while accessing different machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Global &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Setting a global &lt;code&gt;.gitignore&lt;/code&gt; can be really helpful. By default &lt;code&gt;$HOME/.config/git/ignore&lt;/code&gt; is used. You will need to create it if it does not exist on your system. The location of the file can be changed in your &lt;code&gt;~/.gitconfig&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The file works the say way as normal `.gitignore files but for the whole system instead of the individual project.&lt;/p&gt;

&lt;p&gt;The main reason I use it right now is those pesky &lt;code&gt;.DS_Store&lt;/code&gt; files. I'd rather not have to add that to every single one of my projects.&lt;/p&gt;

&lt;p&gt;Documentation on this feature and much more can be found &lt;a href="https://git-scm.com/docs/gitignore"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;.git/info/exclude&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Sometimes you have your own workflow that causes you to need to have git ignore certain files and it's not appropriate to add entries to the project .gitignore.&lt;/p&gt;

&lt;p&gt;I ran into this situation recently. I'm was working on a project where everyone was using PHPStorm and I was the only one using VS Code.&lt;/p&gt;

&lt;p&gt;I added my own &lt;code&gt;.vscode/launch.json&lt;/code&gt; for debugging and I was not ready to add it to the project yet. I kept almost committing it along with my other changes, then I found &lt;code&gt;.git/info/exclude&lt;/code&gt;. It's another file that works the same way as a global or local gitignore, but it's project-specific and not tracked by git. It was exactly what I needed but never knew it was there.&lt;/p&gt;

&lt;p&gt;Documentation on this feature and much more can be found &lt;a href="https://git-scm.com/docs/gitignore"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Checkout Previous Branch
&lt;/h2&gt;

&lt;p&gt;A simple but really useful shortcut is running &lt;code&gt;git checkout -&lt;/code&gt; to switch back to the previously checked out branch. You can keep running it over and over to toggle between two branches.&lt;/p&gt;

&lt;p&gt;Fun fact, this works with other commands like &lt;code&gt;cd -&lt;/code&gt;!&lt;/p&gt;

</description>
      <category>git</category>
      <category>beginners</category>
    </item>
    <item>
      <title>My Development Environment</title>
      <dc:creator>Aaron Ellington</dc:creator>
      <pubDate>Mon, 13 Jan 2020 02:29:31 +0000</pubDate>
      <link>https://forem.com/aaronellington/my-development-environment-5242</link>
      <guid>https://forem.com/aaronellington/my-development-environment-5242</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vjML_p4c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1498050108023-c5249f4df085%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vjML_p4c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1498050108023-c5249f4df085%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="My Development Environment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;My Development Environment is a Ubuntu 18.04 Server VM&lt;/li&gt;
&lt;li&gt;I can connect to it from pretty much any machine using the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack"&gt;VS Code Remote Development&lt;/a&gt; extension. I've used it on:

&lt;ul&gt;
&lt;li&gt;Linux (many distros)&lt;/li&gt;
&lt;li&gt;Mac OS&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Attempt Number 1
&lt;/h2&gt;

&lt;p&gt;I wanted to just use one machine. I could use a laptop and dock it into my home desk or work desk but this had some major issues. I didn't want to be stuck with macOS as my only option. My Linux laptop was not great (battery life/wifi issues/etc.), Windows didn't have WSL 2 yet. Here were some of my bigger issues with this method and using a MacBook Pro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My docking station setup was not super convenient (many cables or flaky dongles/docks).&lt;/li&gt;
&lt;li&gt;Docker on Mac wasn't as good as Docker on Linux&lt;/li&gt;
&lt;li&gt;I didn't like the idea of only have 1 machine. If it was down for any reason I couldn't work.&lt;/li&gt;
&lt;li&gt;Hard to test a long-running process since it would go to sleep every time I closed my laptop when I had to go to a conference room.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Attempt Number 2
&lt;/h2&gt;

&lt;p&gt;Have 3 machines that I use: home desktop (Windows), work desktop (Linux), and laptop (macOS). Being able to sit down and work at any one of them and pick up where I left off at a different one is a bit annoying. Here are some of the problems I've had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Having to commit my changes to git even if they are not ready just to make sure I'll have them available on a different machine.&lt;/li&gt;
&lt;li&gt;Trying to rsync files around because they are required but not tracked in git&lt;/li&gt;
&lt;li&gt;The setup was different for programming environments (Linux/macOS/Windows)&lt;/li&gt;
&lt;li&gt;Making sure all the libraries are installed that I need to work with something new&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Attempt Number 3 (Current)
&lt;/h2&gt;

&lt;p&gt;I was dealing with Attempt Number 2 for some time and then something wonderful happened... &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack"&gt;VS Code Remote Development&lt;/a&gt; was released!&lt;/p&gt;

&lt;p&gt;I've been using VS Code as my primary editor for a while now and this extension has solved most of my problems! I use it to connect my local instance of VS Code to a remote machine over SSH.&lt;/p&gt;

&lt;p&gt;This allowed me to have just one development server that I can connect to from any of my machines. I used a headless install of Ubuntu 18.04 Server as my development server. Then I could work and be productive from any machine that met these requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Able to run VS Code&lt;/li&gt;
&lt;li&gt;Able to SSH into my development server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a Server
&lt;/h2&gt;

&lt;p&gt;You can be creative about what you use as your development environment. Here are some ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 big server&lt;/li&gt;
&lt;li&gt;multiple specialized servers

&lt;ul&gt;
&lt;li&gt;different network access (home network / corporate network)&lt;/li&gt;
&lt;li&gt;different operating systems (Any Linux distro/FreeBSD/Windows/macOS)&lt;/li&gt;
&lt;li&gt;different CPU architectures&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A VM so you can take advantage of snapshotting and easy remote management

&lt;ul&gt;
&lt;li&gt;Self host it on your hypervisor&lt;/li&gt;
&lt;li&gt;Use a VPS provider like Digital Ocean&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A desktop

&lt;ul&gt;
&lt;li&gt;It does not need to be a headless server. You could connect to your work desktop from your home desktop while working from home&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;There are some things to consider before choosing a server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory usage

&lt;ul&gt;
&lt;li&gt;VS Code is running on the remote server. It is NOT just a fancy implementation of sshfs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;VS Code Extensions

&lt;ul&gt;
&lt;li&gt;Many extensions will have to be separately installed on the remote machine even if you have it installed locally.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;network access

&lt;ul&gt;
&lt;li&gt;This only connects VS Code to your remote server, not anything else. So if you are running a web server on your remote server, you might not be able to access it directly. VS Code does offer port forwarding so your web browser on your local machine can access web servers on your remote machine.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;I use a VM on a hypervisor running on my desk at work for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have various snapshots of my VM so I can quickly recover if I mess anything up too badly.&lt;/li&gt;
&lt;li&gt;If my desktop is out of order for any reason (Windows updates/etc.) I'm able to pull my laptop out and pick right up where I left off.&lt;/li&gt;
&lt;li&gt;It has network access to all my companies internal resources (databases/APIs/etc.)&lt;/li&gt;
&lt;li&gt;Only downside: I just need to turn on my VPN if I'm not in the office (not a big deal)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing a Client
&lt;/h2&gt;

&lt;p&gt;At this point our requirements for our client are pretty low:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can run VS Code&lt;/li&gt;
&lt;li&gt;Can SSH into your development server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VS Code runs on all major operating systems (Windows/Linux/macOS) and since it's open-source, others have complied it for most environments (FreeBSD/Chrome OS/Raspberry Pi/etc.)&lt;/p&gt;

&lt;p&gt;This has made me much less picky about my desktop. I've previously wanted to stick to Debian based Linux distros because I'm primarily deploying on Ubunutu servers. But now I'm okay with running anything. I've even started to seriously consider choosing Windows because I'd only be using it for the window management. Then I wouldn't have to worry about a desktop application not supporting Linux.&lt;/p&gt;

&lt;p&gt;I no longer need a powerful desktop or laptop for development. My client can be pretty much anything. Here are some of the things I'm considering trying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Surface Go (with a docking station)

&lt;ul&gt;
&lt;li&gt;Even with WSL 2, I was worried about it being powerful enough... I'm not worried anymore.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Chromebooks

&lt;ul&gt;
&lt;li&gt;Previously I was worried about complicated steps to get everything running correctly... I'm not worried anymore.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Raspberry Pi 4

&lt;ul&gt;
&lt;li&gt;This is probably not a good idea, but I'm curious to try.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I'm sure I'll be experimenting with different clients for a while, but that is okay. The whole point of this is I can quickly get up and running on a new client. All I need to do is install VS Code and gain SSH access to my development server.&lt;/p&gt;

&lt;p&gt;I can only find 2 real downsides to this method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;network access to development server required

&lt;ul&gt;
&lt;li&gt;I'm running into fewer and fewer situations that I don't have access to the internet. Between most of my flights having free internet and my phone hotspot, this is not a concern for me.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;VS Code Lock-In

&lt;ul&gt;
&lt;li&gt;Most, if not all, other editors do not have this functionality so I am pretty reliant on VS Code. This feature has become so important to me that it does not bother me that I'm locked in to only using VS Code.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;I'm really happy with this setup. I feel like it's exactly what I've been waiting for. I love the client/server model in general and I feel this is an excellent use of it.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>texteditor</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Getting Started With Ansible</title>
      <dc:creator>Aaron Ellington</dc:creator>
      <pubDate>Mon, 16 Dec 2019 03:05:09 +0000</pubDate>
      <link>https://forem.com/aaronellington/getting-started-with-ansible-2l70</link>
      <guid>https://forem.com/aaronellington/getting-started-with-ansible-2l70</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oh7sG17G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1558494949-ef010cbdcc31%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oh7sG17G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1558494949-ef010cbdcc31%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Getting Started With Ansible"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have about five Linux machines that I run for personal use and maintaining them has become a chore. In the past, I would manually connect to each of them and run various commands and scripts to ensure they were up-to-date and running okay. Now I use Ansible, and I'm never looking back.&lt;/p&gt;

&lt;p&gt;I want to share a small "getting started" example of how I use it, requiring zero knowledge of Ansible.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I'm probably going to be oversimplifying Ansible and excluding major portions of what it can do. This is only meant to be a getting started guide.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Ansible?
&lt;/h2&gt;

&lt;p&gt;Essentially, it's a method for running shell commands on multiple machines all at once via configuration files, not scripts. One of my favorite features is that you usually do not have to write the shell commands yourself, there are built-in modules to ensure everything is done the best way for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of how to use CLI tools&lt;/li&gt;
&lt;li&gt;1 Control Node (A machine to run ansible from)&lt;/li&gt;
&lt;li&gt;Key-Based SSH access to at least 1 Managed Node (can be the same machine as the Control Node&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Install Ansible
&lt;/h2&gt;

&lt;p&gt;My Control Node is my laptop (currently running Elementary OS), so I installed the Ansible CLI tools via the Ubuntu PPA using the instructions found on Ansible's website here: &lt;a href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html"&gt;https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create Your Inventory
&lt;/h2&gt;

&lt;p&gt;Your inventory is a list of machines you wish to manage. This list can be basic or complicated. I'll be using a basic example here but information on more complex setups can be found here: &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#intro-inventory"&gt;https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#intro-inventory&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, Ansible CLI tools look for your inventory configuration in &lt;code&gt;/etc/ansible/hosts&lt;/code&gt;, but we can tell it to look elsewhere.&lt;/p&gt;

&lt;p&gt;Let's create a new file called &lt;code&gt;inventory.ini&lt;/code&gt; with the hosts you would like to manage grouped any way you'd like. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[exampleGroup1]
192.168.1.2:22
192.168.1.3:22
[exampleGroup2]
192.168.1.4:22
example.com:22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Run Commands Against Your Inventory
&lt;/h2&gt;

&lt;p&gt;Let's try running a command your hosts as a test. We will use the &lt;code&gt;ansible&lt;/code&gt; command with the &lt;code&gt;-i&lt;/code&gt; flag to tell it where your inventory file is. We will also need to specify which group to run the command against, in this case, it will be &lt;code&gt;all&lt;/code&gt;. And then tell it to run the &lt;code&gt;hostname&lt;/code&gt; command on every machine via the &lt;code&gt;-a&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ansible -i inventory.ini all -a "hostname"

192.168.1.2 | CHANGED | rc=0 &amp;gt;&amp;gt;
rasp-pi-01

192.168.1.3 | CHANGED | rc=0 &amp;gt;&amp;gt;
rasp-pi-02

192.168.1.4 | CHANGED | rc=0 &amp;gt;&amp;gt;
rasp-pi-03

example.com | CHANGED | rc=0 &amp;gt;&amp;gt;
exampledotcom-web-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
If you received any errors, address those before proceeding.





&lt;h2&gt;
  
  
  Step 4: Create Ansible Config
&lt;/h2&gt;

&lt;p&gt;I would prefer not to have to specify my inventory file every time I want to use Ansible. Luckily, there is an easy solution for that. By creating an &lt;code&gt;ansible.cfg&lt;/code&gt; file in the same directory you are running your Ansible commands from, you can tell it where to find your inventory allowing you to exclude the &lt;code&gt;-i&lt;/code&gt; flag from your commands.&lt;/p&gt;

&lt;p&gt;Here is what that file should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[defaults]
inventory = inventory.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many other settings are available to be set here, so having this file already in place will come in handy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Create a Playbook
&lt;/h2&gt;

&lt;p&gt;A playbook contains a list of "plays" each having a list of tasks and which hosts to run those tasks on.&lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;update.yml&lt;/code&gt; with these contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Update Servers
  hosts: all
  tasks:
    - name: Upgrade apt Packages
      vars:
        ansible_user: root
      apt:
        upgrade: dist
        update_cache: yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we are defining a play with a single task to use the &lt;code&gt;apt&lt;/code&gt; module to upgrade all the packages on the host. This task essentially runs &lt;code&gt;apt-get update &amp;amp;&amp;amp; apt-get dist-upgrade&lt;/code&gt; on all your hosts as root.&lt;/p&gt;

&lt;p&gt;The module documentation is among some of the best I've seen. Documentation for all of the modules available to you can be found here: &lt;a href="https://docs.ansible.com/ansible/latest/modules/modules_by_category.html"&gt;https://docs.ansible.com/ansible/latest/modules/modules_by_category.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The modules are an amazing part of Ansible. They make running commands like ones above much easier by making sure it runs with the correct flags and variables set so it won't require manual interaction. For example, it adds the &lt;code&gt;-y&lt;/code&gt; flag so I don't have to press &lt;code&gt;Y&lt;/code&gt; then &lt;code&gt;ENTER&lt;/code&gt; to actually to do the upgrades.&lt;/p&gt;

&lt;p&gt;They are extremely powerful and helpful for getting things done with minimal effort from you. Even the &lt;code&gt;apt&lt;/code&gt; module has many more features than shown in this post: &lt;a href="https://docs.ansible.com/ansible/latest/modules/apt_module.html"&gt;https://docs.ansible.com/ansible/latest/modules/apt_module.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Run Your Playbook
&lt;/h2&gt;

&lt;p&gt;In the last step, we created a playbook but now we need to run it. For this, we use a separate command &lt;code&gt;ansible-playbook&lt;/code&gt;. It also uses the &lt;code&gt;ansible.cfg&lt;/code&gt; so it knows where to find our inventory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ansible-playbook update.yml

PLAY [Update Servers] 

TASK [Gathering Facts] 
ok: [192.168.1.2]
ok: [192.168.1.3]
ok: [192.168.1.4]
ok: [example.com]

TASK [Upgrade apt Packages] 
ok: [192.168.1.2]
ok: [192.168.1.3]
ok: [192.168.1.4]
ok: [example.com]

PLAY RECAP 
192.168.1.2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.4 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
example.com : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Play Recap&lt;/code&gt; is a great feature. Especially as you add more tasks, it can be hard to watch or search through hundreds of lines of output to make sure nothing went wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Some Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This is a really basic example of how to use Ansible. Here are some other Ansible features that I use and enjoy but did not include in this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Include Task Lists from other files in Plays so they are reusable: &lt;a href="https://docs.ansible.com/ansible/latest/modules/include_module.html"&gt;https://docs.ansible.com/ansible/latest/modules/include_module.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Loops to run the same task multiple times without duplicating the whole task: &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html"&gt;https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Template to customize a file before pushing it to a host: &lt;a href="https://docs.ansible.com/ansible/latest/modules/template_module.html"&gt;https://docs.ansible.com/ansible/latest/modules/template_module.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Conditionals to skip tasks in certain situations: &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html"&gt;https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My actual update playbook does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update my apt packages (much like the example in this post)&lt;/li&gt;
&lt;li&gt;Use the docker-compose module to pull new images and restart my containers if needed&lt;/li&gt;
&lt;li&gt;Keep my dotfiles git repository updated on all of my hosts&lt;/li&gt;
&lt;li&gt;Check if a reboot is required on any of my hosts and cause a task to fail if they do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Things that were not clear to me at first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If any task fails, that host will be excluded from all future plays and tasks for this run of the playbook. But all other hosts will continue.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ansible</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Importance of Configuring Go sql.DB Connections</title>
      <dc:creator>Aaron Ellington</dc:creator>
      <pubDate>Fri, 04 Jan 2019 02:41:15 +0000</pubDate>
      <link>https://forem.com/aaronellington/importance-of-configuring-go-sqldb-connections-15bc</link>
      <guid>https://forem.com/aaronellington/importance-of-configuring-go-sqldb-connections-15bc</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I've recently started to switch from PHP to Go and I missed something big (but now seems super obvious). My first go at technical blogging was a giant fail:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/aaronellington" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sgXTrVPv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Xc_2Efdl--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/68263/550447ef-eef7-4c27-825d-f2c4d7862a31.png" alt="aaronellington"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/aaronellington/go-sqldb-periodic-error-invalid-connection--35a" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Go sql.DB Periodic Error: invalid connection&lt;/h2&gt;
      &lt;h3&gt;Aaron Ellington ・ Aug 26 '18 ・ 2 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#go&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#sql&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I wanted to loop back around and show what I've learned. I'm also hoping that this time I'm not missing anything as large.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;Coming from PHP, I'm used to scripts starting and stopping pretty frequently and the database connections that were opened would only stay open for a few seconds (max). But when I switched to Go, my scripts were running for days instead of seconds.&lt;/p&gt;

&lt;p&gt;If more than about 15 minutes went by without a MySQL query being executed, the following attempt would fail with a &lt;code&gt;invalid connection&lt;/code&gt; error. In my previous post, I found that calling a &lt;code&gt;db.Ping()&lt;/code&gt; every 5 minutes "mostly" fixed my issue. But as many people kindly pointed out, this was not a good solution. I also still got errors from time to time, but not nearly as often.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution (New!)
&lt;/h2&gt;

&lt;p&gt;Not quite sure how I missed it the first time, but there is a &lt;code&gt;db.SetConnMaxLifetime&lt;/code&gt; function. This allowed the sql package to handle how long connections can last before it will preemptively close them and reopen new ones. The default is to reuse them forever. The documentation puts it best:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go doc sql.DB.SetConnMaxLifetime
func (db *DB) SetConnMaxLifetime(d time.Duration)
    SetConnMaxLifetime sets the maximum amount of time a connection may be
    reused.

    Expired connections may be closed lazily before reuse.

    If d &amp;lt;= 0, connections are reused forever.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I worked with our database team to see how long before the MySQL server starts to kill idle connections and used my newfound &lt;code&gt;SetConnMaxLifetime&lt;/code&gt; function to match the server. Since then, I have received near 0 connection errors over the past few months.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"database/sql"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="s"&gt;"github.com/go-sql-driver/mysql"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user:password@/dbname"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// Error handling omitted for this example&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetConnMaxLifetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Start the web server&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Although this now seems like a obvious concept, it did not when I first start with Go. Hopefully this can help someone else avoid the same mistake that I made.&lt;/p&gt;

</description>
      <category>go</category>
      <category>database</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Go sql.DB Periodic Error: invalid connection</title>
      <dc:creator>Aaron Ellington</dc:creator>
      <pubDate>Sun, 26 Aug 2018 22:04:33 +0000</pubDate>
      <link>https://forem.com/aaronellington/go-sqldb-periodic-error-invalid-connection--35a</link>
      <guid>https://forem.com/aaronellington/go-sqldb-periodic-error-invalid-connection--35a</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Up until now, my team at work were primarily PHP developers. We recently deployed our first Go application (a REST API) to our production server to take care of some of the backend functions of our primary &lt;a href="https://symfony.com/" rel="noopener noreferrer"&gt;Symfony&lt;/a&gt; app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;After 15-20 minutes of inactivity, the next request to our API would fail because of an &lt;code&gt;invalid connection&lt;/code&gt; error when trying to make any database query, but then the next query would be successful.&lt;/p&gt;

&lt;p&gt;This would normally happen overnight when we would have much lower traffic to our API.&lt;/p&gt;

&lt;p&gt;I suspected the idle connections were being killed by the MySQL server. We never had this problem with PHP because a new MySQL connection would be made on each request, or if a PHP script was running for more than 15 minutes it would be constantly making requests keeping the connection alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;I thought of adjusting the timeout on the MySQL server but it is managed for us by another team so I was hoping to find a solution that my team could implement.&lt;/p&gt;

&lt;p&gt;Looking through &lt;code&gt;go doc sql.DB&lt;/code&gt; I saw it had a &lt;code&gt;Ping()&lt;/code&gt; method. The description says: &lt;code&gt;Ping verifies a connection to the database is still alive, establishing a connection if necessary.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That sounded exactly like what would solve my problem, and it did! I just set a simple goroutine to run the &lt;code&gt;Ping()&lt;/code&gt; method every 5 minutes.&lt;/p&gt;

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

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"database/sql"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="s"&gt;"github.com/go-sql-driver/mysql"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user:password@/dbname"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// Error handling omitted for this example&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;periodicPing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Start the web server&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;periodicPing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="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;h2&gt;
  
  
  Update (Jan 3, 2019)
&lt;/h2&gt;

&lt;p&gt;I acknowledge that this was not a good solution and I wanted to come back and reference an update to my issue: &lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/aaronellington" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F68263%2F550447ef-eef7-4c27-825d-f2c4d7862a31.png" alt="aaronellington"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/aaronellington/importance-of-configuring-go-sqldb-connections-15bc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Importance of Configuring Go sql.DB Connections&lt;/h2&gt;
      &lt;h3&gt;Aaron Ellington ・ Jan 4 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#go&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#database&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



</description>
      <category>go</category>
      <category>sql</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
