<?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: bowlerdesign.tech</title>
    <description>The latest articles on DEV Community by bowlerdesign.tech (@edleeman).</description>
    <link>https://dev.to/edleeman</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%2F515512%2F30197589-0f1b-45ab-8437-544d6c1b2373.jpg</url>
      <title>DEV Community: bowlerdesign.tech</title>
      <link>https://dev.to/edleeman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edleeman"/>
    <language>en</language>
    <item>
      <title>Archiving ProtonMail Emails on a headless Ubuntu instance</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Mon, 16 Nov 2020 11:17:00 +0000</pubDate>
      <link>https://dev.to/edleeman/archiving-protonmail-emails-on-a-headless-ubuntu-instance-28pe</link>
      <guid>https://dev.to/edleeman/archiving-protonmail-emails-on-a-headless-ubuntu-instance-28pe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E3tVhfsd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1590247813693-5541d1c609fd%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--E3tVhfsd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1590247813693-5541d1c609fd%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Archiving ProtonMail Emails on a headless Ubuntu instance"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to be able to store all of my &lt;a href="https://protonmail.com/"&gt;ProtonMail&lt;/a&gt; emails locally for archival purposes, with the intention of running a local content search whenever I needed something retrieving.&lt;/p&gt;

&lt;p&gt;I'm going to talk through my steps. There's plenty of tutorials out there for email services such as; Gmail, Hotmail etc. But not much around ProtonMail, especially using a headless instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  ProtonMail bridge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://protonmail.com/bridge/"&gt;ProtonMail Bridge&lt;/a&gt; is an open-source piece of software built by the Proton Team to create a dummy IMAP server locally, with a sole purpose of decrypting your emails locally to enable you to use a desktop email client such as &lt;a href="https://www.thunderbird.net/en-US/"&gt;ThunderBird&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ProtonMail Bridge is a paid feature though, with a minimum requirement of the &lt;a href="https://protonmail.com/pricing"&gt;ProtonMail Plus&lt;/a&gt; plan ($5/month). ProtonMail Bridge is not available on the ProtonMail Free plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;We're going to set up  a Cron job on a headless Ubuntu instance to periodically pull down all our emails. This instance will be running ProtonMail Bridge and a tool named &lt;a href="http://www.offlineimap.org/"&gt;Offlineimap&lt;/a&gt;, which will be used to store all your emails in a plaintext, searchable format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Setting up your Linux server
&lt;/h3&gt;

&lt;p&gt;You'll need to either have an existing server instance or create one. I use a Proxmox instance running on a server in my loft. You could also use something like &lt;a href="https://www.digitalocean.com/"&gt;Digital Ocean&lt;/a&gt; to run your local archive. Using the following link will give you $100 worth of credits for 60 days to play around with, just sign up using &lt;a href="https://m.do.co/c/d2a3afe52625"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have the server set up, or have logged in. You'll need to do some updates and run some prerequisite installs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: gpg and pass
&lt;/h3&gt;

&lt;p&gt;To use ProtonMail Bridge, you need to first set up the dependencies. These dependencies are &lt;code&gt;pass&lt;/code&gt; (A password management system), which has a dependency of &lt;code&gt;gpg&lt;/code&gt; (A key management system).&lt;/p&gt;

&lt;h4&gt;
  
  
  Installing GPG
&lt;/h4&gt;

&lt;p&gt;GPG should be installed on your system as part of the &lt;code&gt;update and upgrade&lt;/code&gt; commands that you initially ran after provisioning your server. If it's not, you can install GPG by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once GPG is installed, you need to generate a GPG key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg --gen-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run you through a wizard of adding your full name and email address. Also requiring you to create a passphrase for your gpg key. Add a password to satisfy the wizard, but we'll have to remove this later.&lt;/p&gt;

&lt;p&gt;Unfortunately, ProtonMail Bridge will not work with a password protected GPG key in headless mode. This is a serious security risk, so make up your own opinion on this, do some research on what implications this has.&lt;/p&gt;

&lt;p&gt;Once GPG has finished, you'll have a public key output. It'll look something like the following&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;We now need to remove the passphrase associated with this key.&lt;/p&gt;

&lt;p&gt;Run the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg --edit-key 7BDD29402175BC627671356BE8AC4A1C3C5J6357
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have executed that command, you'll be in the context of gpg&lt;/p&gt;

&lt;p&gt;Run then next command&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll now need to enter the passphrase you created earlier, which will unlock the GPG key.&lt;/p&gt;

&lt;p&gt;There will now be another console prompt to enter a new passphrase.&lt;/p&gt;

&lt;p&gt;Leave this blank.&lt;/p&gt;

&lt;p&gt;You'll be asked to confirm that you want a blank passphrase, it'll warn you about the security implications.&lt;/p&gt;

&lt;p&gt;Hit 'Y' or Yes to confirm this implication.&lt;/p&gt;

&lt;p&gt;Finally, quit the GPG context by typing &lt;code&gt;q&lt;/code&gt; and hitting Enter.&lt;/p&gt;

&lt;h4&gt;
  
  
  Installing Pass
&lt;/h4&gt;

&lt;p&gt;Now it's time to install Pass, which is a ProtonMail Bridge dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll then need to initialise a new &lt;code&gt;pass&lt;/code&gt; instance with your previously generated gpg key id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pass init 7BDD29402175BC627671356BE8AC4A1C3C5J6357
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's, all the setup required for initialising &lt;code&gt;pass&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Installing ProtonMail Bridge
&lt;/h3&gt;

&lt;p&gt;Now it's time to install ProtonMail Bridge.&lt;/p&gt;

&lt;p&gt;Run the following command to install the latest version of ProtonMail Bridge at time of writing this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget https://protonmail.com/download/beta/protonmail-bridge_1.5.0-1_amd64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dpkg -i protonmail-bridge_1.5.0-1_amd64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There may be some more missing dependencies when installing. To fix this, run the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt --fix-broken install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Initialising ProtonMail Bridge
&lt;/h3&gt;

&lt;p&gt;We now need to log in to ProtonMail Bridge, to do this, run the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protonmail-bridge --cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ProtonMail Bridge will start up and ask you to log in with an email and a password.&lt;/p&gt;

&lt;p&gt;This will be your standard ProtonMail username and password.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Getting your ProtonMail IMAP Username and Password
&lt;/h3&gt;

&lt;p&gt;Whilst in the context of the ProtonMail Bridge session, run the following command.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll see an output like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            Welcome to ProtonMail Bridge interactive shell
                              ___....___
    ^^ __..-:'':__ :..: __:'':-..__
                  _.-: __:.-:'': : : :'':-.:__ :-._
                .':.-: : : : : : : : : :._:'.
             _ :.': : : : : : : : : : : :'.: _
            []: : : : : : : : : : : : : :[]
            []: : : : : : : : : : : : : :[]
   :::::::::[]: __:__ : __:__ : __:__ : __:__ : __:__ : __:__ :__:[]:::::::::::
   !!!!!!!!![]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!![]!!!!!!!!!!!
   ^^^^^^^^^[]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[]^^^^^^^^^^^
            [] []
            [] []
      jgs [] []
    ~~^_~^~/ \~^-~^~ _~^-~_^~-^~_^~~ -^~_~^~-~_~-^~_^/ \~^ ~~_ ^
&amp;gt;&amp;gt;&amp;gt; info
Configuration for {your email}
IMAP Settings
Address: 127.0.0.1
IMAP port: 1143
Username: {your email}
Password: {your password}
Security: STARTTLS

SMTP Settings
Address: 127.0.0.1
IMAP port: 1025
Username: {your email}
Password: {your password}
Security: STARTTLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a note of the values &lt;code&gt;{your email}&lt;/code&gt; and &lt;code&gt;{your password}&lt;/code&gt;. You'll need these later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Setting up Offlineimap
&lt;/h3&gt;

&lt;p&gt;I'm going to be using an open-source tool I found on Github. Written by &lt;code&gt;peterrus&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run the following command to pull down the repo from Github.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/peterrus/protonmail-export-linux.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;cd&lt;/code&gt; into the cloned repo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd protonmail-export-linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you need to install &lt;code&gt;offlineimap&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install offlineimap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, in the &lt;code&gt;protonmail-export-linux&lt;/code&gt; directory, there will be a &lt;code&gt;offlineimaprc&lt;/code&gt; file. Open this with your favorite text editor. I'll be using &lt;code&gt;vim&lt;/code&gt; in this example.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll see a file open that looks something like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Feel free to modify this file to your needs
# See https://github.com/OfflineIMAP/offlineimap/blob/master/offlineimap.conf for a full reference

[general]
accounts = Protonmail
metadata = ./offlineimap-metadata

[Account Protonmail]
localrepository = ProtonmailLocal
remoterepository = ProtonmailRemote

# Feel free to change this, or disable it by commenting it out
postsynchook = notify-send 'Protonmail export done'

[Repository ProtonmailLocal]
type = Maildir
localfolders = ./protonmail-export
# If you (accidentally) delete mail locally, it won't get synced to Protonmail
sync_deletes = no

[Repository ProtonmailRemote]
type = IMAP
# Change this to the value provided in the Protonmail bridge
remoteuser = {your email}
remotepass = {your password}
remotehost = localhost
remoteport = 1143
ssl = no
starttls = no
# If you delete mail on Protonmail, this deletion also get's synced to the archive
expunge = yes
# Don't try to sync local changes to Protonmail, we just want a backup
readonly = True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave everything how it is, apart from the section where I have specified, &lt;code&gt;{your email}&lt;/code&gt; and &lt;code&gt;{your password}&lt;/code&gt;. These will need to be replaced with your ProtonMail Bridge email and password that I got you to take note of earlier.&lt;/p&gt;

&lt;p&gt;Save your changes and exit your text editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Setting up screen
&lt;/h3&gt;

&lt;p&gt;I use screen to run services in the background. Both &lt;code&gt;protonmail-bridge&lt;/code&gt; and &lt;code&gt;offlineimap&lt;/code&gt; need to run simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install screen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Creating a startup script
&lt;/h3&gt;

&lt;p&gt;I made it easy for myself and created a startup script to execute both &lt;code&gt;protonmail-bridge&lt;/code&gt; and &lt;code&gt;offlineimap&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here's my script, simply create a new file named &lt;code&gt;start-sync.sh&lt;/code&gt; in your home directory. Giving it execute permissions by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chmod +x start-sync.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;start-sync.sh&lt;/code&gt; file contents should look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#! /bin/bash
echo "killing existing screen sessions"
pkill screen
echo "Starting bridge"
screen -d -m protonmail-bridge --cli
echo "Starting sync"
sleep 10
screen -d -m offlineimap -c /home/{your user}/protonmail-export-linux/offlineimaprc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the location of your &lt;code&gt;offlineimaprc&lt;/code&gt; file in that shell script.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Starting Offlineimap
&lt;/h3&gt;

&lt;p&gt;Now it's time to execute your &lt;code&gt;start-sync.sh&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./start-sync.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, both of your services should have started up. To see them, run the following command&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll see an output like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ed@mail:~# screen -r
There are several suitable screens on:
        6415..mail (12/05/20 18:06:26) (Detached)
        6308..mail (12/05/20 18:06:16) (Detached)
Type "screen [-d] -r [pid.]tty.host" to resume one of them.
ed@mail:~#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -r 6415 // 6415 being the id of the top process on the screen -r output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output of a successful authentication with ProtonMail Bridge, and a list of emails currently being downloaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Copy message UID 2081 (23/3445) ProtonmailRemote:All Mail -&amp;gt; ProtonmailLocal
Folder INBOX [acc: Protonmail]:
etc...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detach from this screen by hitting &lt;code&gt;Ctrl + a&lt;/code&gt; and &lt;code&gt;Ctrl + d&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9: Where are my emails?
&lt;/h3&gt;

&lt;p&gt;Whilst your emails are downloading, they will be saved to &lt;code&gt;~/protonmail-export&lt;/code&gt;. You should see a list of your email folders in this directory, under the &lt;code&gt;cur&lt;/code&gt; subdirectory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 10: Setting up a cron job to pull new emails
&lt;/h3&gt;

&lt;p&gt;Now it's time to set this job up to run each day, or each hour, whatever you'd like to pick.&lt;/p&gt;

&lt;p&gt;Run the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo crontab -e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the very bottom of your &lt;code&gt;crontab&lt;/code&gt; file, paste in the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 0 * * * /home/{your user}/start-sync.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use a tool called &lt;a href="https://crontab.guru/"&gt;crontab.guru&lt;/a&gt; to assist with picking a schedule to run my command.&lt;/p&gt;

&lt;p&gt;My command will run at midnight each day.&lt;/p&gt;




&lt;h3&gt;
  
  
  That's all
&lt;/h3&gt;

&lt;p&gt;And hopefully, everything should be up and running successfully. Let me know if you receive any issues in to comments, I'll be happy to help you out.&lt;/p&gt;

&lt;p&gt;You could now sync all your emails to Dropbox, or something like Syncthing, which &lt;a href="https://dev.to/edleeman/how-to-set-up-a-headless-syncthing-network-5h7k-temp-slug-1061506"&gt;I have recently written about here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>selfhosting</category>
      <category>headless</category>
      <category>linux</category>
    </item>
    <item>
      <title>Schiit Modi 2 - Fixing Windows not recognizing USB device</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Sun, 27 Sep 2020 16:31:02 +0000</pubDate>
      <link>https://dev.to/edleeman/schiit-modi-2-fixing-windows-not-recognizing-usb-device-mj0</link>
      <guid>https://dev.to/edleeman/schiit-modi-2-fixing-windows-not-recognizing-usb-device-mj0</guid>
      <description>&lt;h2&gt;
  
  
  Sorry for the off-topic post, but this has been bugging me for months
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JhPlKGdL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1522338140262-f46f5913618a%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--JhPlKGdL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1522338140262-f46f5913618a%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Schiit Modi 2 - Fixing Windows not recognizing USB device"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having recently purchased a Schiit Modi 2 from eBay, just to try out before commiting to something a little more expensive (and reliable).&lt;/p&gt;

&lt;p&gt;The Modi 2 has some real issues with Windows 10, I belive the Modi 3 solves all of these issues.&lt;/p&gt;

&lt;p&gt;There's an error message when plugging the Modi into Windows which is something like "USB device not recognized, the last USB device you connected to this computer malfunctioned, and Windows does not recognize it."&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;A hairdryer... yes, I'm being serious. Apply heat directly to the Schiit Modi 2 for 10 - 15 seconds. Then try connecting the Modi to your PC. Ridiculous, I know, but it worked.&lt;/p&gt;

&lt;p&gt;Now, I can't take any credit for this solution, the real MVP is &lt;a href="https://www.audiosciencereview.com/forum/index.php?members/vavvo966.5405/"&gt;vavvo966&lt;/a&gt; who posted &lt;a href="https://www.audiosciencereview.com/forum/index.php?threads/please-help-schiit-modi-2-not-being-recognized-anymore-by-windows.5943/page-2#post-136701"&gt;the following&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's another method which includes replacing the chip, but this involves soldering 100 tiny legs, which I'm not a fan of, but you can find some information &lt;a href="https://www.audiosciencereview.com/forum/index.php?threads/please-help-schiit-modi-2-not-being-recognized-anymore-by-windows.5943/page-5#post-468079"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Self-hosting a Wireguard VPN, the easy way</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Sun, 13 Sep 2020 19:49:07 +0000</pubDate>
      <link>https://dev.to/edleeman/self-hosting-a-wireguard-vpn-the-easy-way-1ddl</link>
      <guid>https://dev.to/edleeman/self-hosting-a-wireguard-vpn-the-easy-way-1ddl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VC71vHu---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1542577195-d562c6698ff3%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--VC71vHu---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1542577195-d562c6698ff3%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Self-hosting a Wireguard VPN, the easy way"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're going to cover setting up a Wireguard VPN on your home server or cloud service. For secure remote access to your internal network, or a cheap, secure connection to a cloud service for &lt;em&gt;some&lt;/em&gt; increased privacy when browsing online.&lt;br&gt;&lt;br&gt;
VPN's don't make you anonymous, there's a lot of stigma around this. Here's some &lt;a href="https://www.privacytools.io/providers/vpn/"&gt;helpful information&lt;/a&gt; if you want to read into this some more.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up Wireguard, the easy way
&lt;/h2&gt;

&lt;p&gt;I initially found setting up Wireguard confusing. Keys kept getting mixed up, I had no way of sending public keys between devices so that I could set up a client on my mobile device etc..&lt;/p&gt;

&lt;p&gt;After around 30 seconds of lazy Duck Duck Go'ing (Not quite the same ring to it). I found a script on Github provided by &lt;a href="https://github.com/angristan"&gt;angristan&lt;/a&gt;. Here's &lt;a href="https://github.com/angristan/wireguard-install"&gt;the repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's just a bash script which does all of the config for you, but still providing user prompts for entering the public server IP and choosing a preferred DNS address for the server.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Clone and execute the Wireguard Installer
&lt;/h3&gt;

&lt;p&gt;After ssh'ing to your server, whether it's local, or cloud-hosted. If you're thinking of cloud hosting your Wireguard VPN for some privacy, I'd highly recommend using &lt;a href="https://www.digitalocean.com/"&gt;Digital Ocean&lt;/a&gt;. Using the following link will give you $100 worth of credits for 60 days to play around with, just sign up using &lt;a href="https://m.do.co/c/d2a3afe52625"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyway, after you have accessed your machine, we need to pull down the Wireguard installer code from Github. We're just going to &lt;code&gt;curl&lt;/code&gt; it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -O https://raw.githubusercontent.com/angristan/wireguard-install/master/wireguard-install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we just need to change the file permissions to allow execution of the new &lt;code&gt;.sh&lt;/code&gt; scipt we've just downloaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chmod +x wireguard-install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, execute the Wireguard Installer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ./wireguard-install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configuring Wireguard
&lt;/h3&gt;

&lt;p&gt;This is the easy part.&lt;/p&gt;

&lt;p&gt;You'll see below the process of setting up Wireguard using the Wireguard Installer. All of the values below were picked for me, I just had to hit &lt;code&gt;return&lt;/code&gt; a few times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ Ed sudo ./wireguard-install.sh
Welcome to the WireGuard installer!
The git repository is available at: https://github.com/angristan/wireguard-install

I need to ask you a few questions before starting the setup.
You can leave the default options and just press enter if you are ok with them.

IPv4 or IPv6 public address: 37.120.198.182
Public interface: eth2
WireGuard interface name: wg0
Server's WireGuard IPv4: 10.66.66.1
Server's WireGuard IPv6: fd42:42:42::1
Server's WireGuard port [1-65535]: 57281
First DNS resolver to use for the clients: 176.103.130.130
Second DNS resolver to use for the clients (optional): 176.103.130.131

Okay, that was all I needed. We are ready to setup your WireGuard server now.
You will be able to generate a client at the end of the installation.
Press any key to continue...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running through those steps above, the Wireguard Installer will do its thing and set up Wireguard for you. You'll eventually be left with a prompt to set up a new client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tell me a name for the client.
The name must consist of alphanumeric character. It may also include an underscore or a dash.
Client name: Phone
Client's WireGuard IPv4: 10.66.66.2
Client's WireGuard IPv6: fd42:42:42::2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's my config, I just entered a name and the rest was generated for me.&lt;/p&gt;

&lt;p&gt;What's also really cool, is that a QR code gets generated in the console window, which you can scan with your new device.&lt;/p&gt;

&lt;p&gt;You'll also have a &lt;code&gt;.conf&lt;/code&gt; file generated for you to copy to your device.&lt;/p&gt;

&lt;h3&gt;
  
  
  Portforwarding
&lt;/h3&gt;

&lt;p&gt;Remember we had to specify a port? We'll need to forward that. There are a million different tutorials on the web for how to port-forward for your router.&lt;/p&gt;

&lt;p&gt;Here's a &lt;a href="https://portforward.com/"&gt;handy guide.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final steps
&lt;/h3&gt;

&lt;p&gt;All that's now left to do is to set up Wireguard on your device. Simply download the required app/program onto your machine and either scan the provided QR code or import that &lt;code&gt;.conf&lt;/code&gt; file into your client.&lt;/p&gt;

&lt;p&gt;Then enable your VPN. Let me know how it goes.&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>wireguard</category>
      <category>linux</category>
    </item>
    <item>
      <title>How to self-host Bitwarden Password Manager</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Sun, 30 Aug 2020 18:10:05 +0000</pubDate>
      <link>https://dev.to/edleeman/how-to-self-host-bitwarden-password-manager-a38</link>
      <guid>https://dev.to/edleeman/how-to-self-host-bitwarden-password-manager-a38</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kH_W0vrg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1582139329536-e7284fece509%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--kH_W0vrg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1582139329536-e7284fece509%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="How to self-host Bitwarden Password Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article will cover setting up your own self-hosted Bitwarden instance with Docker and configuring ngnix to allow for public exposure for cross-device access to your vault.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Bitwarden?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://bitwarden.com/"&gt;Bitwarden&lt;/a&gt;is a free and open-source password management service that stores sensitive information such as website credentials in an encrypted vault. The Bitwarden platform offers a&lt;a href="https://bitwarden.com/download/"&gt;variety of client applications&lt;/a&gt;including a web interface, desktop applications, browser extensions, mobile apps, and a CLI.&lt;/p&gt;

&lt;p&gt;I use Bitwarden as my main password vault. It stores my card details for automating the filling out of payment forms. Saves me from having to find or remember my card details. I also use Bitwarden for storing all of my passwords.&lt;/p&gt;

&lt;p&gt;Having Bitwarden as a public endpoint means that I can connect to my password vault using the Bitwarden app on Android, specifying my self hosted instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the Bitwarden Server
&lt;/h2&gt;

&lt;p&gt;This section of the tutorial is to set up the main Bitwarden 'hub'. This will be a publicly exposed Bitwarden API that will live on your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Setting up your Linux server
&lt;/h3&gt;

&lt;p&gt;You'll need to either have an existing server instance or create one. I use a Proxmox instance running on a server in my loft. You could also use something like &lt;a href="https://www.digitalocean.com/"&gt;Digital Ocean&lt;/a&gt; to host your Bitwarden Server. Using the following link will give you $100 worth of credits for 60 days to play around with, just sign up using &lt;a href="https://m.do.co/c/d2a3afe52625"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have the server set up, or have logged in. You'll need to do some updates and run some prerequisite installs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade
sudo apt-get install docker.io
sudo apt-get install docker-compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Provisioning your Bitwarden Server
&lt;/h3&gt;

&lt;p&gt;Next, you'll need to create a new folder, this will house your Bitwarden Server, you can call it anything memorable. I'll just call mine &lt;code&gt;bitwarden&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~
mkdir bitwarden
cd bitwarden
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll need to create a &lt;code&gt;docker-compose.yml&lt;/code&gt; file. This is an orchistration file which &lt;code&gt;docker-compose&lt;/code&gt; will use to provision your Docker instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll need to edit your &lt;code&gt;docker-compose.yml&lt;/code&gt; file and paste in the following content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# docker-compose.yml
version: '3'

services:
  bitwarden:
    image: bitwardenrs/server
    restart: always
    ports:
      - 80:80
    volumes:
      - ./bw-data:/data
    environment:
      WEBSOCKET_ENABLED: 'true' # Required to use websockets
      SIGNUPS_ALLOWED: 'true' # set to false to disable signups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using &lt;a href="https://github.com/dani-garcia/bitwarden_rs"&gt;bitwarden_rs&lt;/a&gt; as it's written in Rust, faster and more reliable. Also entirely opensource with a heavy user-base.&lt;/p&gt;

&lt;p&gt;Save your &lt;code&gt;docker-compose.yml&lt;/code&gt; file and exit back to your &lt;code&gt;bitwarden&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Running your Bitwarden Server locally
&lt;/h3&gt;

&lt;p&gt;Now, you have everything provisioned for running your Bitwarden Server.&lt;/p&gt;

&lt;p&gt;The next thing to do is run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start up your Bitwarden Server inside Docker, it may take some time to pull down the images.&lt;/p&gt;

&lt;p&gt;You can eventually see your instance running by executing the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will list your running instance.&lt;/p&gt;

&lt;p&gt;If all is well, you can locally view your Bitwarden Server by navigating to &lt;code&gt;http://localhost:PORT&lt;/code&gt;. Or from another machine by using your ip address instead of &lt;code&gt;localhost&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see something that looks like the following.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lAixzv8B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-homepage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lAixzv8B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-homepage.JPG" alt="How to self-host Bitwarden Password Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, you'll just need to register for an account on your new hosted instance.&lt;/p&gt;

&lt;p&gt;Click the &lt;code&gt;Create Account&lt;/code&gt; button&lt;/p&gt;

&lt;p&gt;Then fill out your details. If you have an existing Bitwarden account, you'll still have to create a new account on this instance. You can then Export and Import between accounts.&lt;/p&gt;

&lt;p&gt;The last thing to do is hit &lt;code&gt;Submit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZSLg2boz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-register.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZSLg2boz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-register.JPG" alt="How to self-host Bitwarden Password Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Exposing your new server publicly
&lt;/h3&gt;

&lt;p&gt;This part may sound scary, but it is required to allow your Bitwarden Clients (Android, iOS, Chrome extension etc) to connect to your server.&lt;/p&gt;

&lt;p&gt;We're going to be using nginx.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up nginx
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.nginx.com/"&gt;Nginx&lt;/a&gt; is a reverse proxy that allows you to point incoming web traffic to your new Bitwardeb server.&lt;/p&gt;

&lt;p&gt;Firstly, install nginx if you haven't already&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have UFW installed, you will have to Allow Nginx through your local firewall.&lt;/p&gt;

&lt;p&gt;I have a tutorial for &lt;a href="https://dev.to/edleeman/setting-up-ufw-on-ubuntu-server-2ama-temp-slug-4815620"&gt;setting up UFW here&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ufw app list

Output
---

Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there are three profiles available for Nginx:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nginx Full&lt;/strong&gt; : This profile opens both port 80 (normal, unencrypted web traffic) and port 443 (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx HTTP&lt;/strong&gt; : This profile opens only port 80 (normal, unencrypted web traffic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx HTTPS&lt;/strong&gt; : This profile opens only port 443 (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is recommended that you enable the most restrictive profile that will still allow the traffic you’ve configured. Since we will be configuring SSL for our server we will need to allow traffic on port 443.&lt;/p&gt;

&lt;p&gt;You can enable this by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ufw allow 'Nginx HTTPS'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next thing to do is just double check your nginx server is up and running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl status nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something that looks like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: active (running)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next part allows us to take incoming traffic and point it to your container instance. Allowing you to expose your Bitwarden  server to the internet.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;/etc/nginx/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /etc/nginx/

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

&lt;/div&gt;



&lt;p&gt;Use your favorite text editor and open the following file with sudo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim nginx.conf

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

&lt;/div&gt;



&lt;p&gt;I use the following code for my syncing server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 443;
    listen [::]:443;
    server_name vault.bowlerdesign.tech;

    location / {
        proxy_pass http://127.0.0.1:3000; # bitwarden server address
    }
}

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Port-forwarding
&lt;/h4&gt;

&lt;p&gt;You will need to port forward your instance to allow public access to your instance. This will involve googling how to port forward from your router.&lt;/p&gt;

&lt;p&gt;You'll need to &lt;a href="https://portforward.com/"&gt;point port 443 to your instance&lt;/a&gt; where nginx is set up.&lt;/p&gt;

&lt;h4&gt;
  
  
  Linking Bitwarden Server with your public domain
&lt;/h4&gt;

&lt;p&gt;You will also need to set up a public domain name. This can then be used to call your new public instance with port 443 exposed.&lt;/p&gt;

&lt;p&gt;For example, I would set up a subdomain on &lt;code&gt;bowlerdesign.tech&lt;/code&gt; to be &lt;code&gt;vault.bowlerdesign.tech&lt;/code&gt;. Notice this is also the domain I specified in my nginx config above.&lt;/p&gt;

&lt;p&gt;Here's something to search for with regards to &lt;a href="https://www.google.com/search?client=firefox-b-d&amp;amp;q=point+domain+name+to+server"&gt;setting up a domain name&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up Certbot
&lt;/h4&gt;

&lt;p&gt;Certbot allows us to generate SSL certificates for free with Let's Encrypt. It's simple to install and use. Even hooks in with nginx, meaning that there's no more manual configuration required.&lt;/p&gt;

&lt;p&gt;To install Certbot, simply run the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install certbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, to set up your SSL certificate, run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Follow the instructions, select your domain name from the nginx list.&lt;br&gt;&lt;br&gt;
Also, select &lt;code&gt;redirect&lt;/code&gt; as this will upgrade any http requests to https.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Connecting to your new Bitwarden instance from a client.
&lt;/h3&gt;

&lt;p&gt;I'm going to use the Firefox Bitwarden Plugin for this part of the tutorial. But the process is identical for all Bitwarden clients.&lt;/p&gt;

&lt;p&gt;First, if you haven't already, install your chosen Bitwarden client and open it.&lt;/p&gt;

&lt;p&gt;In the top left corner, click the cog icon&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZuVVWChf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-app-homepage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZuVVWChf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-app-homepage.JPG" alt="How to self-host Bitwarden Password Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll then get some configuration. Simply add your full url into the &lt;code&gt;Server URL&lt;/code&gt; field&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oLMuaN-X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-enter-url.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oLMuaN-X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-enter-url.JPG" alt="How to self-host Bitwarden Password Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like so, then just hit &lt;code&gt;Save&lt;/code&gt; and log in as normal&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aI_dU0SM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-save-url.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aI_dU0SM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/BW-save-url.JPG" alt="How to self-host Bitwarden Password Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  That's it
&lt;/h3&gt;

&lt;p&gt;Pretty easy right?&lt;/p&gt;

&lt;p&gt;Please don't hesitate to get in touch in the comments if you get stuck. I'd be more than happy to help out with any issues you may face.&lt;/p&gt;

&lt;p&gt;Also, if this helped, please consider buying me a beer! It helps with server costs and providing these blog posts.&lt;/p&gt;

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

</description>
      <category>ubuntu</category>
      <category>bitwarden</category>
      <category>linux</category>
    </item>
    <item>
      <title>How to completely self host Standard Notes</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Tue, 25 Aug 2020 18:29:25 +0000</pubDate>
      <link>https://dev.to/edleeman/how-to-completely-self-host-standard-notes-1aja</link>
      <guid>https://dev.to/edleeman/how-to-completely-self-host-standard-notes-1aja</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--POoDbjVL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1517842645767-c639042777db%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--POoDbjVL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1517842645767-c639042777db%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="How to completely self host Standard Notes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article will cover setting up your own self-hosted Standard Notes instance and routing your instance through nginx to allow for public exposure.&lt;br&gt;&lt;br&gt;
We'll also go over self-hosting Standard Notes Extensions to allow you to use extensions within your instance, such as a Markdown editor or secure spreadsheets&lt;/p&gt;
&lt;h3&gt;
  
  
  What is Standard Notes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://standardnotes.org/"&gt;Standard Notes&lt;/a&gt; is a free, open-source, and completely encrypted notes app. Being open-source, allows anyone to self-host their own Standard Notes server. Which means that you own all of your data, on your server.&lt;/p&gt;

&lt;p&gt;Standard Notes is great for a secure, private, encrypted note-taking solution. Even without self-hosting, all notes are E2E (end-to-end) encrypted. This means that nobody, other than yourself, can view the notes that you have written.&lt;/p&gt;

&lt;p&gt;I have written about Standard Notes in my &lt;a href="https://dev.to/edleeman/my-self-hosted-note-syncing-journey-once-switching-to-iphone-48kg-temp-slug-233251"&gt;My Self Hosted Note Syncing Journey Once Switching to Iphone&lt;/a&gt; blog post.&lt;/p&gt;

&lt;p&gt;Self-hosting Standard Notes is probably for the paranoid and the curious. It was the latter!&lt;/p&gt;

&lt;p&gt;It's relatively simple and gives you peace of mind knowing that only you own your data.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up Standard Notes Syncing Server
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Setting up the syncing server
&lt;/h3&gt;

&lt;p&gt;You'll need to set up a new Linux server. I'll be using a Ubuntu Server instance which I self-host in &lt;a href="https://proxmox.com/en/"&gt;Proxmox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This new server will need to have Docker and Docker Compose installed.&lt;/p&gt;

&lt;p&gt;These can be installed by running the following commands on your server after you have ssh'd to your server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade
sudo apt-get install docker.io
sudo apt-get install docker-compose

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

&lt;/div&gt;



&lt;p&gt;Next, you'll need clone the syncing-server repo down to your home directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/standardnotes/syncing-server.git

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

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;syncing-server&lt;/code&gt; directory, you'll need to create a new file named &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd syncing-server
touch docker-compose.yml

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

&lt;/div&gt;



&lt;p&gt;Open this file with your favorite editor, I use Vim.&lt;/p&gt;

&lt;p&gt;You'll need to paste the following yaml code into the &lt;code&gt;docker-compose.yml&lt;/code&gt; file. This is what Docker Compose reads to provision your Docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
  app:
    build: .
    depends_on:
      - db
    command: bash -c "bundle exec rails db:migrate &amp;amp;&amp;amp; bundle exec rails server -b 0.0.0.0"
    env_file: .env
    restart: unless-stopped
    ports:
      - ${EXPOSED_PORT}:3000
  db:
    image: mysql:5.7
    environment:
        MYSQL_DATABASE: '${DB_DATABASE}'
        MYSQL_USER: '${DB_USERNAME}'
        MYSQL_PASSWORD: '${DB_PASSWORD}'
        MYSQL_ROOT_PASSWORD: '${DB_ROOT_PASSWORD}'
    expose:
      - '3306'
    restart: always
    volumes:
      - std_notes_db:/var/lib/mysql
volumes:
  std_notes_db:

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

&lt;/div&gt;



&lt;p&gt;Save &lt;code&gt;docker-compose.yml&lt;/code&gt; and exit back to the folder.&lt;/p&gt;

&lt;p&gt;You'll also need the create a &lt;code&gt;.env&lt;/code&gt; file. This is where your environment variables will be stored.&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Again, edit this file with your favorite editor and paste in the following.&lt;br&gt;
&lt;/p&gt;

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

EXPOSED_PORT=3000
RAILS_ENV=production
RAILS_LOG_TO_STDOUT=false

# Database Settings

DB_PORT=3306
DB_HOST=127.0.0.1
DB_DATABASE=standard_notes_db
DB_USERNAME=std_notes_user

# Please change this!
DB_PASSWORD=changeme123

# Please change this!
DB_ROOT_PASSWORD=changeme123

DB_POOL_SIZE=30
DB_WAIT_TIMEOUT=180

SECRET_KEY_BASE=changeme123

# Disable user registration
#DISABLE_USER_REGISTRATION=true

# Datadog
DATADOG_ENABLED=false

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

&lt;/div&gt;



&lt;p&gt;Save and exit that file.&lt;/p&gt;

&lt;p&gt;Now, ensure that you're in the &lt;code&gt;syncing-server&lt;/code&gt; directory and run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d

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

&lt;/div&gt;



&lt;p&gt;This will start docker, using the docker-compose.yml file and run the server in detached mode.&lt;/p&gt;

&lt;p&gt;You should hopefully have an instance running at &lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your MySQL Data will be written to your local disk at /var/lib/mysql - Be sure to back this up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Setting up Nginx
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.nginx.com/"&gt;Nginx&lt;/a&gt; is a reverse proxy that allows you to point incoming web traffic to your new Standard Notes syncing server. If you only intend on using Standard Notes locally, you can skip this section entirely.&lt;/p&gt;

&lt;h4&gt;
  
  
  Installation
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install nginx

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

&lt;/div&gt;



&lt;p&gt;If you have UFW installed, you will have to Allow Nginx through your local firewall.&lt;/p&gt;

&lt;p&gt;I have a tutorial for &lt;a href="https://dev.to/edleeman/setting-up-ufw-on-ubuntu-server-2ama-temp-slug-4815620"&gt;setting up UFW here&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ufw app list


Output
---

Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH

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

&lt;/div&gt;



&lt;p&gt;As you can see, there are three profiles available for Nginx:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nginx Full&lt;/strong&gt; : This profile opens both port 80 (normal, unencrypted web traffic) and port 443 (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx HTTP&lt;/strong&gt; : This profile opens only port 80 (normal, unencrypted web traffic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx HTTPS&lt;/strong&gt; : This profile opens only port 443 (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is recommended that you enable the most restrictive profile that will still allow the traffic you’ve configured. Since we haven’t configured SSL for our server yet in this guide, we will only need to allow traffic on port 80.&lt;/p&gt;

&lt;p&gt;You can enable this by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ufw allow 'Nginx HTTP'

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Checking your webserver is up and running
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl status nginx

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

&lt;/div&gt;



&lt;p&gt;You should see something that looks like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: active (running)
...

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Pointing Nginx to your syncing server
&lt;/h3&gt;

&lt;p&gt;The next part allows us to take incoming traffic and point it to your container instance. Allowing you to expose your syncing server to the internet.&lt;/p&gt;

&lt;p&gt;This will allow you to hook up Standard Notes on any device and sync your notes privately.&lt;/p&gt;

&lt;p&gt;If someone gets hold of your public endpoint, your notes will still be safe as they are encrypted with your Standard Notes username and password.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;/etc/nginx/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /etc/nginx/

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

&lt;/div&gt;



&lt;p&gt;Use your favorite text editor and open the following file with sudo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim nginx.conf

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

&lt;/div&gt;



&lt;p&gt;I use the following code for my syncing server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;
    server_name notes.bowlerdesign.tech;

    location / {
        proxy_pass http://127.0.0.1:3000; # syncing-server address
    }
}

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Port-forwarding
&lt;/h3&gt;

&lt;p&gt;You will need to port forward your instance to allow public access to your instance. This will involve googling how to port forward from your router.&lt;/p&gt;

&lt;p&gt;You'll need to &lt;a href="https://portforward.com/"&gt;point port 80 to your instance&lt;/a&gt; where nginx is set up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Linking Standard Notes with your public domain
&lt;/h3&gt;

&lt;p&gt;You will also need to set up a public domain name. This can then be used to call your new public instance with port 80 exposed.&lt;/p&gt;

&lt;p&gt;For example, I would set up a subdomain on &lt;code&gt;bowlerdesign.tech&lt;/code&gt; to be &lt;code&gt;notes.bowlerdesign.tech&lt;/code&gt;. Notice this is also the domain I specified in my nginx config above.&lt;/p&gt;

&lt;p&gt;Here's something to search for with regards to &lt;a href="https://www.google.com/search?client=firefox-b-d&amp;amp;q=point+domain+name+to+server"&gt;setting up a domain name&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up Certbot
&lt;/h4&gt;

&lt;p&gt;Certbot allows us to generate SSL certificates for free with Let's Encrypt. It's really really simple to install and use. Even hooks in with nginx, meaning that there's no more manual configuration required.&lt;/p&gt;

&lt;p&gt;To install Certbot, simply run the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install certbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, to set up your SSL certificate, run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Follow the instructions, select your domain name from the nginx list.&lt;br&gt;&lt;br&gt;
Also select &lt;code&gt;redirect&lt;/code&gt; as this will upgrade any http requests to https.&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 6: Connecting to your Standard Notes syncing server from Standard Notes
&lt;/h3&gt;

&lt;p&gt;Congratulations on making it this far, now it's time to hook up Standard Notes with your new private syncing server.&lt;/p&gt;

&lt;p&gt;Standard Notes is available on multiple platforms. The easiest way for this tutorial is to use their web interface.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;a href="https://app.standardnotes.org/"&gt;https://app.standardnotes.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll be greeted with something that looks like the following screenshot&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BBWSsNkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-dashboard.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BBWSsNkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-dashboard.JPG" alt="How to completely self host Standard Notes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will need to sign up with a new account as this will be syncing to your server.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Register&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Before filling out your registration credentials, select &lt;code&gt;Advanced Options&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QPLWoh0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-advanced-options.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QPLWoh0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-advanced-options.JPG" alt="How to completely self host Standard Notes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now fill in your new server url or IP address.&lt;/p&gt;

&lt;p&gt;Then go back to the registration form and enter an Email and Password&lt;/p&gt;

&lt;p&gt;Finally, click &lt;code&gt;Register&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Easy right? You now have a working self-hosted Standard Notes server.&lt;/p&gt;


&lt;h2&gt;
  
  
  Setting up self-hosted Standard Notes extensions
&lt;/h2&gt;

&lt;p&gt;Now that you have Standard Notes working, you can add and delete notes at your heart's desire. But wouldn't it be cool to also use the Standard Notes Extensions to fully embrace the power of Standard Notes?&lt;/p&gt;

&lt;p&gt;You can leave it here, or follow the below process to self-host your extensions too, utilising the power of Markdown editors or Secure Spreadsheets!&lt;/p&gt;

&lt;p&gt;Firstly, by self-hosting your extensions, you're bypassing the need to purchase a license. The Standard Notes team mainly works from a 'Visionary' point of view. I heavily encourage you to purchase a license to support the great work that the team are doing. Here's a quote from the Standard Notes team themselves.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Most of our extensions are &lt;a href="https://github.com/sn-extensions"&gt;open-source&lt;/a&gt; and available for self-hosting. You can also learn to develop your own extensions by following the guides on this site. However, we encourage you to support the sustainability and future development of this project by &lt;a href="https://standardnotes.org/extensions"&gt;purchasing a subscription&lt;/a&gt;."&lt;/em&gt; ~ &lt;a href="https://docs.standardnotes.org/extensions/intro#sustainability"&gt;Sustainability of Standard Notes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If however, you would still like to proceed with hosting your Extensions, then please follow the steps below.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Pulling the Standard Notes Extensions Repository
&lt;/h3&gt;

&lt;p&gt;I will be using the Standard Notes Extention repository by &lt;a href="https://github.com/iganeshk"&gt;iganeshk&lt;/a&gt; who has provided their own &lt;a href="https://github.com/iganeshk/standardnotes-extensions/blob/master/README.md"&gt;README.md&lt;/a&gt; for setting up Standard Notes Extensions. I'll be using their instructions in my steps below. All credit for the setup goes to &lt;a href="https://github.com/iganeshk"&gt;iganeshk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's begin.&lt;/p&gt;

&lt;p&gt;Firstly, start by ssh'ing into your new server that we set up in the previous section.&lt;/p&gt;

&lt;p&gt;We'll need to pull down the required repository&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/iganeshk/standardnotes-extensions.git
cd standardnotes-extensions

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

&lt;/div&gt;



&lt;p&gt;Ensure that you have Python3 and pip installed on your server as this is used to install the required dependencies for the extensions repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install -y python3 python3-pip

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

&lt;/div&gt;



&lt;p&gt;Then use pip3 to install the required dependencies. You may have to reboot before this command works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install -r requirements.txt

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configuring the Standard Notes Extensions Repository
&lt;/h3&gt;

&lt;p&gt;You'll now need to create a &lt;code&gt;.env&lt;/code&gt; file to store your environment variables for the repository.&lt;/p&gt;

&lt;p&gt;Ensure you're in the &lt;code&gt;standardnotes-extensions&lt;/code&gt; directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~/standardnotes-extensions

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

&lt;/div&gt;



&lt;p&gt;Then create the &lt;code&gt;.env&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Then open with your favorite editor&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Paste in the following example from &lt;code&gt;iganeshk&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/iganeshk/standardnotes-extensions/blob/master/env.sample"&gt;env.sample&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Edit your domain value, and Github username and TOKEN&lt;/p&gt;

&lt;p&gt;The Github integration is to allow you to clone down the Standard Notes extensions from the &lt;a href="https://github.com/sn-extensions"&gt;Standard Notes Github&lt;/a&gt; page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Github Credentials
# Generate your token here: https://github.com/settings/tokens
# No additional permission required, this is just to avoid github api rate limits
#

domain: https://domain.com/extensions

github:
  username: USERNAME
  token: TOKEN

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

&lt;/div&gt;



&lt;p&gt;Save and close that file. Ensuring that it is named &lt;code&gt;.env&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Running Standard Notes Extensions from your server
&lt;/h3&gt;

&lt;p&gt;You'll now need to execute the service.&lt;/p&gt;

&lt;p&gt;This can be done by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 build_repo.py

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

&lt;/div&gt;



&lt;p&gt;This will generate a &lt;code&gt;/public&lt;/code&gt; directory, which you will serve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Routing with nginx
&lt;/h3&gt;

&lt;p&gt;Now, with that static folder generated, we just need to point nginx to that &lt;code&gt;/public&lt;/code&gt; folder to serve the extensions.&lt;/p&gt;

&lt;p&gt;As before in the previous section, edit your nginx config&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;/etc/nginx/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /etc/nginx/

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

&lt;/div&gt;



&lt;p&gt;Use your favorite text editor and open the following file with sudo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim nginx.conf

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

&lt;/div&gt;



&lt;p&gt;You'll see your previously added nginx config for serving the syncing server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;
    server_name notes.bowlerdesign.tech;

    location / {
        proxy_pass http://127.0.0.1:3000; # syncing-server address
    }
}

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

&lt;/div&gt;



&lt;p&gt;Under the server section, you will need to add the following configuration, again taken from &lt;code&gt;iganeshk&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;location ^~ /extensions {
    autoindex off;
    alias /path/to/standardnotes-extensions/public;
    # CORS HEADERS
    if ($request_method = 'OPTIONS') {
       add_header 'Access-Control-Allow-Origin' '*';
       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
       #
       # Custom headers and headers various browsers *should* be OK with but aren't
       #
       add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
       #
       # Tell client that this pre-flight info is valid for 20 days
       #
       add_header 'Access-Control-Max-Age' 1728000;
       add_header 'Content-Type' 'text/plain; charset=utf-8';
       add_header 'Content-Length' 0;
       return 204;
    }
    if ($request_method = 'POST') {
       add_header 'Access-Control-Allow-Origin' '*';
       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
       add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
       add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    }
    if ($request_method = 'GET') {
       add_header 'Access-Control-Allow-Origin' '*';
       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
       add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
       add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    }
  }

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

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;nginx.conf&lt;/code&gt; should now look like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;
    server_name notes.bowlerdesign.tech;

    location / {
        proxy_pass http://127.0.0.1:3000; # syncing-server address
    }

    location ^~ /extensions {
    autoindex off;
    alias /path/to/standardnotes-extensions/public;
    # CORS HEADERS
    if ($request_method = 'OPTIONS') {
       add_header 'Access-Control-Allow-Origin' '*';
       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
       #
       # Custom headers and headers various browsers *should* be OK with but aren't
       #
       add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
       #
       # Tell client that this pre-flight info is valid for 20 days
       #
       add_header 'Access-Control-Max-Age' 1728000;
       add_header 'Content-Type' 'text/plain; charset=utf-8';
       add_header 'Content-Length' 0;
       return 204;
    }
    if ($request_method = 'POST') {
       add_header 'Access-Control-Allow-Origin' '*';
       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
       add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
       add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    }
    if ($request_method = 'GET') {
       add_header 'Access-Control-Allow-Origin' '*';
       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
       add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
       add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    }
  }
}

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

&lt;/div&gt;



&lt;p&gt;Make sure you change the alias to point to your public folder. Remember, this is in the &lt;code&gt;/standardnotes-extensions/public&lt;/code&gt; location.&lt;/p&gt;

&lt;p&gt;For me it would be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias /home/ubuntu/standardnotes-extensions/public

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

&lt;/div&gt;



&lt;p&gt;Close and save the &lt;code&gt;nginx.conf&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;The last thing you'll need to do in terms of setup is to verify the endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Verifying the Standard Notes Extensions endpoint
&lt;/h3&gt;

&lt;p&gt;Navigate to your previous url and add &lt;code&gt;extensions/index.json&lt;/code&gt; to the end of the url.&lt;/p&gt;

&lt;p&gt;For example&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://notes.bowlerdesign.tech/extensions/index.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see a list of json objects.&lt;/p&gt;

&lt;p&gt;If, you don't see the endpoint correctly, ensure that the &lt;code&gt;/public&lt;/code&gt; folder has the correct permissions from nginx to read the files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Adding Extensions to Standard Notes
&lt;/h3&gt;

&lt;p&gt;Nearly there. We just need to add our new endpoint to our existing Standard Notes client.&lt;/p&gt;

&lt;p&gt;Navigate back to &lt;code&gt;https://app.standardnotes.org&lt;/code&gt; and log in as we did before.&lt;/p&gt;

&lt;p&gt;In the bottom corner, select &lt;code&gt;Extensions&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PE5yCSUU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-extensions.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PE5yCSUU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-extensions.JPG" alt="How to completely self host Standard Notes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll see a field that reads &lt;code&gt;Enter Extended Code&lt;/code&gt; this is where you'll usually paste your Extensions API Key. But all you need to do is paste your new extensions url.&lt;/p&gt;

&lt;p&gt;Then hit &lt;code&gt;Submit Code&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jXIyazeZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-extensions-submit.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jXIyazeZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/08/SN-extensions-submit.JPG" alt="How to completely self host Standard Notes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  And, we're done
&lt;/h3&gt;

&lt;p&gt;That's it, providing everything worked, you should now be able to add extensions to your Standard Notes instance.&lt;/p&gt;

&lt;p&gt;Now, this process is complicated, so please don't hesitate to get in touch in the comments if you get stuck. I'd be more than happy to help out with any issues you may face.&lt;/p&gt;

&lt;p&gt;Thanks for reading, please donate to Standard Notes as, without them, none of this would be possible.&lt;/p&gt;

&lt;p&gt;Also, if this helped, please consider buying me a beer! It helps with server costs and providing these blog posts.&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>standardnotes</category>
      <category>linux</category>
    </item>
    <item>
      <title>How to Set Up a Headless Syncthing Network</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Mon, 03 Aug 2020 18:31:00 +0000</pubDate>
      <link>https://dev.to/edleeman/how-to-set-up-a-headless-syncthing-network-2552</link>
      <guid>https://dev.to/edleeman/how-to-set-up-a-headless-syncthing-network-2552</guid>
      <description>&lt;h2&gt;
  
  
  Overview of Syncthing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://syncthing.net/"&gt;Syncthing&lt;/a&gt; is a P2P (peer to peer) network that allows you to keep your files synchronised.&lt;/p&gt;

&lt;p&gt;Syncthing aims to replace services such as: Dropbox, Google Drive and OneDrive. By putting the user in control of their own data. Syncthing enables the user to set up multiple 'nodes' which can communicate with eachother.&lt;/p&gt;

&lt;p&gt;A node can be, your personal computer, a work machine, a machine in another location or even a central server.&lt;/p&gt;

&lt;p&gt;Syncthing will encrypt all data when it is being transported to your other nodes. Nodes don't just have to be on your local network, you can sync between nodes over the internet. Encrypted, of-course.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case
&lt;/h2&gt;

&lt;p&gt;I use Syncthing as my storage solution. I have a 'central' Syncthing server which lives in my loft. I quoted 'central' as the loft server is just a node, there's nothing special about it other than having a backup cron job running on it.&lt;/p&gt;

&lt;p&gt;I then have multiple other nodes, such as: my main laptop, a work machine and even my Android phone. I have spoken about Syncthing on Android in my &lt;a href="https://dev.to/edleeman/my-self-hosted-note-syncing-journey-once-switching-to-iphone-48kg-temp-slug-233251"&gt;My Self Hosted Note Syncing Journey Once Switching to Iphone&lt;/a&gt; post.&lt;/p&gt;

&lt;p&gt;With the multiple nodes set up, I can choose folders that can sync. I'm currently writing this blog post on my personal laptop, which is syncing to my blog server as I type (well, hit save).&lt;/p&gt;

&lt;p&gt;You can then specify which nodes, the folder you created, to be shared with. So in my case, I am sharing my blog with my personal laptop and my 'central' server, just to backup my data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting up Syncthing
&lt;/h2&gt;

&lt;p&gt;I'm going to show you how to set up Syncthing in the context of my setup. So having that 'central' server.&lt;/p&gt;

&lt;p&gt;My 'central' server is running Ubuntu Server, so the following instructions are tailored for Debian distros.&lt;/p&gt;

&lt;p&gt;Firstly, you need to install the correct package locations in order to download Syncthing correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install curl apt-transport-https

curl -s https://syncthing.net/release-key.txt | sudo apt-key add -

echo "deb https://apt.syncthing.net/ syncthing stable" | sudo tee /etc/apt/sources.list.d/syncthing.list

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

&lt;/div&gt;



&lt;p&gt;Next, update your local repository with the new Syncthing repository&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade

sudo apt-get install syncthing

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

&lt;/div&gt;



&lt;p&gt;The next thing you want to do is enable Syncthing as a system service. This will allow start-up on boot and continuous execution in the background of your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl enable syncthing@username.service

sudo systemctl start syncthing@username.service

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

&lt;/div&gt;



&lt;p&gt;So this will start Syncthing on your Ubuntu Server. Problem is, Ubuntu Server is usually headless. So at the moment there is no way to access the Syncthing GUI to configure your folders and nodes.&lt;/p&gt;

&lt;p&gt;The way we can change this to allow local access from another machine is to change the serve address.&lt;/p&gt;

&lt;p&gt;When you ran the Syncthing command above, a &lt;code&gt;syncthing&lt;/code&gt; folder was created under your &lt;code&gt;/home/username/.config&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Inside that will be a &lt;code&gt;config.xml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Open this with a text editor, something like Vim or Nano. It really doesn't matter.&lt;/p&gt;

&lt;p&gt;Within &lt;code&gt;config.xml&lt;/code&gt; there is a section that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
&amp;lt;gui enabled="true" tls="false" debugging="false"&amp;gt;
    &amp;lt;address&amp;gt;127.0.0.1:8384&amp;lt;/address&amp;gt;
    &amp;lt;apikey&amp;gt;...&amp;lt;/apikey&amp;gt;
    &amp;lt;theme&amp;gt;default&amp;lt;/theme&amp;gt;
&amp;lt;/gui&amp;gt;
...

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

&lt;/div&gt;



&lt;p&gt;Under the address attribute, you need to update the ip address.&lt;br&gt;&lt;br&gt;
&lt;code&gt;0.0.0.0&lt;/code&gt; will allow all machines on your local network to be able to access the Syncthing GUI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
&amp;lt;gui enabled="true" tls="false" debugging="false"&amp;gt;
    &amp;lt;address&amp;gt;0.0.0.0:8384&amp;lt;/address&amp;gt;
    &amp;lt;apikey&amp;gt;...&amp;lt;/apikey&amp;gt;
    &amp;lt;theme&amp;gt;default&amp;lt;/theme&amp;gt;
&amp;lt;/gui&amp;gt;
...

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

&lt;/div&gt;



&lt;p&gt;Save and close &lt;code&gt;config.xml&lt;/code&gt; and then restart Syncthing by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart syncthing@username.service

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Second Node
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;For Syncthing to work, you need to set up another Syncthing node. Simply follow the above steps again on another machine, or jump to the bottom of this page quickly and install Syncthing on Windows or Android&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Accessing the Syncthing GUI
&lt;/h2&gt;

&lt;p&gt;Now with all that running, you should be able to access the Syncthing GUI by navigating to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://{your_server_ip}:8384

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

&lt;/div&gt;



&lt;p&gt;Once entering your ip-address or hostname, you should be presented with something that looks like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KVMQPeZw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-gui.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KVMQPeZw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-gui.jpeg" alt="syncthing-gui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Devices (nodes)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BkVKd68C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-remote-device.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BkVKd68C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-remote-device.jpeg" alt="syncthing-remote-device"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add a new Device, select the &lt;code&gt;Add Remote Device&lt;/code&gt; button, which will open up a new popup window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a6FEcNgv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-add-device-1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a6FEcNgv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-add-device-1.jpeg" alt="syncthing-add-device"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Add Device&lt;/code&gt; window will pop up. You may notice that there is an existing ID which is available to click.&lt;br&gt;&lt;br&gt;
Syncthing automatically polls your local network looking for existing nodes, depending on how your other nodes are set up, you may or may not see this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--djLO-nBV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-device-id-1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--djLO-nBV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-device-id-1.jpeg" alt="syncthing-device-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't see the ID, simply navigate to your other Syncthing node (let's call it &lt;code&gt;Syncthing B&lt;/code&gt;), select &lt;code&gt;Actions &amp;gt; Show ID&lt;/code&gt; from the top right menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ntpXAGcV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-device-id-qr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ntpXAGcV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-device-id-qr.jpeg" alt="syncthing-device-id-qr"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you will see the &lt;code&gt;Syncthing B&lt;/code&gt; Syncthing ID. Copy this ID or use the QR code to scan the ID. And paste the ID into the &lt;code&gt;Device ID&lt;/code&gt; field back on the &lt;code&gt;Syncthing A&lt;/code&gt; node.&lt;/p&gt;

&lt;p&gt;Complicated at first, but once you get the hang of Syncthing, it'll be easier.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Save&lt;/code&gt; on that window and it'll add &lt;code&gt;Syncthing B&lt;/code&gt; as a new device.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharing Folders
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8UQXi0hW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-new-folder.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8UQXi0hW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-new-folder.jpeg" alt="syncthing-new-folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, on &lt;code&gt;Syncthing A&lt;/code&gt; click the &lt;code&gt;Add Folder&lt;/code&gt; button on the left hand side of the screen. This will pop up with a new window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IirrgmKD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-new-folder-window.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IirrgmKD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-new-folder-window.jpeg" alt="syncthing-new-folder-window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your new folder a name and change the path to be relative to the folder on your machine that you want to backup. I tend to keep all of my synced folders under &lt;code&gt;/home/ed/Syncthing&lt;/code&gt; just so that I know where everything is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k4fB1VRo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-sharing-tab.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k4fB1VRo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-sharing-tab.jpeg" alt="syncthing-sharing-tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;code&gt;Sharing Tab&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZQ9rjUib--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-share-with.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZQ9rjUib--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-share-with.jpeg" alt="syncthing-share-with"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the devices you have added. I have a couple more than you probably will just because this is my existing system. Simply select the device that you added and click &lt;code&gt;Save&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accepting the new device
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yh-YZkTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-new-device.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yh-YZkTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-new-device.jpeg" alt="syncthing-new-device"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, on &lt;code&gt;Syncthing B&lt;/code&gt;, there will be a new call-to-action at the top of the page. This is a request for you to allow &lt;code&gt;Syncthing A&lt;/code&gt; to add &lt;code&gt;Syncthing B&lt;/code&gt; as a device. Simply click &lt;code&gt;Add&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PmurY-5E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-add-folder.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PmurY-5E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-add-folder.jpeg" alt="syncthing-add-folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, shortly after accepting the new device, you'll get another popup which is &lt;code&gt;Syncthing A&lt;/code&gt; requesting to share the folder you have just create.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Add&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wkvFBup1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-saved-folder.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wkvFBup1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bowlerdesign.tech/content/images/2020/09/syncthing-saved-folder.jpeg" alt="syncthing-saved-folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, you now need to choose a location for this folder to sync to.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Save&lt;/code&gt; and that's it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Feel free to test by adding a file to the &lt;code&gt;Syncthing A&lt;/code&gt; folder and wait for the &lt;code&gt;global state&lt;/code&gt; to change on &lt;code&gt;Syncthing B&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting up other Syncthing nodes
&lt;/h2&gt;

&lt;p&gt;Syncthing works on everything apart from iPhone. &lt;a href="https://forum.syncthing.net/t/ios-iphone-crowdfunding-on-bountysource/13136"&gt;There are whispers of something coming to iOS in the future&lt;/a&gt;, but not right now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up another node on a Linux machine
&lt;/h3&gt;

&lt;p&gt;Setting up a node on another Linux machine is as simple as just copying the steps above. If the machine isn't headless, such as a desktop machine, you can ignore the part about changing the ip address in the &lt;code&gt;config.xml&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up another node on a Windows machine
&lt;/h3&gt;

&lt;p&gt;Setting up on Windows is relativley easy. You can either go the manual route of &lt;a href="https://syncthing.net/downloads/"&gt;downloading the Syncthing exe&lt;/a&gt;. Or by opting for the simple solution which is installing &lt;a href="https://github.com/canton7/SyncTrayzor"&gt;SyncTrayzor&lt;/a&gt; which adds a system tray icon and runs Syncthing in the backgroud automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up another node on an Android device
&lt;/h3&gt;

&lt;p&gt;Syncthing has apps in the &lt;a href="https://play.google.com/store/apps/details?id=com.nutomic.syncthingandroid"&gt;Play Store&lt;/a&gt; and &lt;a href="https://f-droid.org/packages/com.nutomic.syncthingandroid/"&gt;F-Droid&lt;/a&gt; which can be downloaded and installed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Let me know if this has helped in the comments below.&lt;/p&gt;

&lt;p&gt;Also, feel free to check out my other blog posts &lt;a href="https://bowlerdesign.tech/"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Configuring Vim for Node Js Development</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Tue, 28 Jul 2020 18:58:00 +0000</pubDate>
      <link>https://dev.to/edleeman/configuring-vim-for-node-js-development-4224</link>
      <guid>https://dev.to/edleeman/configuring-vim-for-node-js-development-4224</guid>
      <description>&lt;h2&gt;
  
  
  Using Vim
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T4hirbQa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1517694712202-14dd9538aa97%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--T4hirbQa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1517694712202-14dd9538aa97%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Configuring Vim for Node Js Development"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have come across this article, I probably don't need to explain why you need to use Vim as your development IDE.&lt;br&gt;&lt;br&gt;
You're probably here because you want to use Vim, but not sure how to integrate it into your current workflow.&lt;/p&gt;

&lt;p&gt;I was in the same position a few months ago.&lt;/p&gt;

&lt;p&gt;The easiest way is to install a vim extension into your current IDE of choice, this will bring all of the features of vim into your current workflow.&lt;br&gt;&lt;br&gt;
After getting used to Vim, in something that you're familiar with. You'll probably want to jump-ship into full-blown Vim.&lt;/p&gt;

&lt;p&gt;That's what this blog post is for.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Primeagen
&lt;/h3&gt;

&lt;p&gt;A lot of my configuration and interest in jumping over to Vim came from &lt;a href="https://www.youtube.com/channel/UC8ENHE5xdFSwx71u3fDH5Xw"&gt;The Primeagen&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
This guy is a machine with Vim.&lt;/p&gt;

&lt;p&gt;He's currently a Netflix Engineer who only works in Vim for his day-to-day work. Regularly streaming over on &lt;a href="https://www.twitch.tv/theprimeagen"&gt;Twitch&lt;/a&gt; and uploading on &lt;a href="https://www.youtube.com/channel/UC8ENHE5xdFSwx71u3fDH5Xw"&gt;YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;He has recently produced a series on &lt;a href="https://www.youtube.com/playlist?list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R"&gt;Vim As Your Editor&lt;/a&gt;. I highly recommend that you go and watch it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Plugins
&lt;/h2&gt;

&lt;p&gt;Vim is highly configurable with user-created plugins.&lt;br&gt;&lt;br&gt;
Here's a list that I recommend to use with Node.Js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/tpope/vim-fugitive"&gt;vim-fugitive&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This integrates Git seemlessly into Vim&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/scottw/coc-nvim-1a7f"&gt;coc.nvim&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This allows code completion and intellisense for Javascript in Vim&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/preservim/nerdcommenter"&gt;nerdcommenter&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A simple plugin to comment out lines of code using Vim bindings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/dense-analysis/ale"&gt;ale&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A syntax highlighter for Vim&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/preservim/nerdtree"&gt;nerdtree&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A file tree view for selecting files and navigating folder structure in your projects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/itchyny/lightline.vim"&gt;lightline&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A lightweight status bar to show progress in your buffer and other useful information&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/junegunn/fzf.vim"&gt;fzf&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A file-finding plugin to quickly search your project and swtich buffers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/morhetz/gruvbox"&gt;gruvbox&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;The only theme you should be using. Heavily influenced by The Primeagen&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ap/vim-buftabline"&gt;vim-buftabline&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Introduces 'tabs' into Vim for keeping track of your open buffers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Installing plugins is easy in Vim. I use a plugin manager called &lt;a href="https://github.com/junegunn/vim-plug"&gt;vim-plug&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  .vimrc.plug
&lt;/h2&gt;

&lt;p&gt;Your plugin installation lives in a file called &lt;code&gt;.vimrc.plug&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a copy of my &lt;code&gt;.vimrc.plug&lt;/code&gt; as 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;" .vimrc.plug

call plug#begin('~/.vim/plugged')
" Git
Plug 'tpope/vim-fugitive'

" Code completion
Plug 'neoclide/coc.nvim', {'branch': 'release'}

" Code commenter
Plug 'preservim/nerdcommenter'
"
" Syntax highlighting
Plug 'dense-analysis/ale'

" NERDTree
Plug 'preservim/nerdtree'
"
" Statusbar
Plug 'itchyny/lightline.vim'
"
" Finder
Plug 'junegunn/fzf', { 'do': { -&amp;gt; fzf#install() } }
Plug 'junegunn/fzf.vim'

" File finder
Plug 'vifm/vifm.vim'

" Theme
Plug 'morhetz/gruvbox'

" Tabs
Plug 'ap/vim-buftabline'

call plug#end()

set background=dark
colorscheme gruvbox

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

&lt;/div&gt;



&lt;p&gt;Just install those plugins using &lt;code&gt;:PlugInstall&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  .vimrc
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.vimrc&lt;/code&gt; is your main Vim config file. You probably know this already.&lt;/p&gt;

&lt;p&gt;Here's mine:&lt;br&gt;
&lt;/p&gt;

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

set guicursor=
set noshowmatch
set relativenumber
set nohlsearch
set hidden
set noerrorbells
set tabstop=4 softtabstop=4
set shiftwidth=4
set expandtab
set smartindent
set nu
set nowrap
set smartcase
set noswapfile
set nobackup
set undodir=~/.vim/undodir
set undofile
set incsearch
set termguicolors
set scrolloff=8
set modifiable

" Give more space for displaying messages.
set cmdheight=2

" Having longer updatetime (default is 4000 ms = 4 s) leads to noticeable
" delays and poor user experience.
set updatetime=50

" Don't pass messages to |ins-completion-menu|.
set shortmess+=c

set colorcolumn=100
highlight ColorColumn ctermbg=0 guibg=lightgrey

map &amp;lt;C-n&amp;gt; :NERDTreeToggle&amp;lt;CR&amp;gt;
map &amp;lt;C-p&amp;gt; :Files&amp;lt;CR&amp;gt;
map &amp;lt;C-f&amp;gt; :Rg&amp;lt;CR&amp;gt;
map &amp;lt;C-t&amp;gt; :e &amp;lt;cfile&amp;gt;&amp;lt;cr&amp;gt;
map &amp;lt;S-Tab&amp;gt; :bn&amp;lt;CR&amp;gt;
map &amp;lt;F5&amp;gt; :setlocal spell! spelllang=en_gb&amp;lt;CR&amp;gt;

let loaded_matchparen = 1
let mapleader = " "

" CoC
" GoTo code navigation.
nmap &amp;lt;leader&amp;gt;gd &amp;lt;Plug&amp;gt;(coc-definition)
nmap &amp;lt;leader&amp;gt;gy &amp;lt;Plug&amp;gt;(coc-type-definition)
nmap &amp;lt;leader&amp;gt;gi &amp;lt;Plug&amp;gt;(coc-implementation)
nmap &amp;lt;leader&amp;gt;gr &amp;lt;Plug&amp;gt;(coc-references)
nmap &amp;lt;leader&amp;gt;rr &amp;lt;Plug&amp;gt;(coc-rename)
nmap &amp;lt;leader&amp;gt;g[ &amp;lt;Plug&amp;gt;(coc-diagnostic-prev)
nmap &amp;lt;leader&amp;gt;g] &amp;lt;Plug&amp;gt;(coc-diagnostic-next)
nmap &amp;lt;silent&amp;gt; &amp;lt;leader&amp;gt;gp &amp;lt;Plug&amp;gt;(coc-diagnostic-prev-error)
nmap &amp;lt;silent&amp;gt; &amp;lt;leader&amp;gt;gn &amp;lt;Plug&amp;gt;(coc-diagnostic-next-error)
nnoremap &amp;lt;leader&amp;gt;cr :CocRestart

" Sweet Sweet FuGITive
nmap &amp;lt;leader&amp;gt;gj :diffget //3&amp;lt;CR&amp;gt;
nmap &amp;lt;leader&amp;gt;gf :diffget //2&amp;lt;CR&amp;gt;
nmap &amp;lt;leader&amp;gt;gs :G&amp;lt;CR&amp;gt;

" Search and replace hotkey
nnoremap H :%s//gc&amp;lt;left&amp;gt;&amp;lt;left&amp;gt;&amp;lt;left&amp;gt;

" Move highlighted text up and down
vnoremap J :m '&amp;gt;+1&amp;lt;CR&amp;gt;gv=gv
vnoremap K :m '&amp;lt;-2&amp;lt;CR&amp;gt;gv=gv

" Import plugins
if filereadable(expand("~/.vimrc.plug"))
    source ~/.vimrc.plug
endif

" Status bar config
set statusline+=%#warningmsg#

" Fix files automatically on save
let g:ale_fixers = {}
let g:ale_javascript_eslint_use_global = 1
let g:ale_linters = {
  \'javascript': ['eslint'],
  \'vue': ['eslint', 'stylelint', 'tsserver'],
\}

let g:ale_fixers = {
  \'javascript': ['prettier', 'eslint'],
  \'vue': ['eslint', 'stylelint'],
\}

let g:ale_linters_explicit = 1
let g:ale_sign_column_always = 1
let g:ale_sign_error = '&amp;gt;&amp;gt;'
let g:ale_sign_warning = '--'
let g:ale_fix_on_save = 1

" Close NERDTree when closing the last buffer
autocmd bufenter * if (winnr("$") == 1 &amp;amp;&amp;amp; exists("b:NERDTree") &amp;amp;&amp;amp; b:NERDTree.isTabTree()) | q | endif

fun! TrimWhitespace()
    let l:save = winsaveview()
    keeppatterns %s/\s\+$//e
    call winrestview(l:save)
endfun

autocmd BufWritePre * :call TrimWhitespace()

command! -bang -nargs=* Rg
  \ call fzf#vim#grep(
  \ 'rg --column --line-number --no-heading --color=always --smart-case -- '.shellescape(&amp;lt;q-args&amp;gt;), 1,
  \ fzf#vim#with_preview(), &amp;lt;bang&amp;gt;0)

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

&lt;/div&gt;



&lt;p&gt;There is some initial setup prior to using this configuration. Such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running &lt;code&gt;:CocInstall coc-tsserver&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Installing fzf &lt;code&gt;sudo apt-get install fzf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Installing Rg &lt;code&gt;sudo apt-get install ripgrep&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final points
&lt;/h2&gt;

&lt;p&gt;Vim is a steep learning curve. Get used to Vim by using a plugin in your existing IDE, once comfortable, switch to Vim.&lt;/p&gt;

&lt;p&gt;Let me know if this post has helped you in the comments below, I'd appreciate your feedback.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting up Cucumber, Nightwatch.js and Selenium Grid for highly scalable remote Chrome execution from Buddy</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Tue, 21 Jul 2020 11:26:00 +0000</pubDate>
      <link>https://dev.to/edleeman/setting-up-cucumber-nightwatch-js-and-selenium-grid-for-highly-scalable-remote-chrome-execution-from-buddy-320i</link>
      <guid>https://dev.to/edleeman/setting-up-cucumber-nightwatch-js-and-selenium-grid-for-highly-scalable-remote-chrome-execution-from-buddy-320i</guid>
      <description>&lt;h2&gt;
  
  
  Technical Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nightwatchjs.org/"&gt;Nightwatch.js&lt;/a&gt; - &lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; powered end-to-end testing framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cucumber.io/"&gt;Cucumber&lt;/a&gt; - BDD testing framework that sits on top of Nightwatch.js&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.selenium.dev/"&gt;Selenium Grid&lt;/a&gt; - Highly scalable browser automation for running tests&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://buddy.works/"&gt;Buddy&lt;/a&gt; - CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  End Goal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IBzjYwHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1555099962-4199c345e5dd%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--IBzjYwHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1555099962-4199c345e5dd%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Setting up Cucumber, Nightwatch.js and Selenium Grid for highly scalable remote Chrome execution from Buddy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The outcome of setting up this pipeline will allow us to execute end-to-end tests from anywhere, in any development environment.&lt;/p&gt;

&lt;p&gt;Having Selenium Grid as a public endpoint also allows us to execute these end-to-end tests from our existing CI/CD pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selenium Grid
&lt;/h2&gt;

&lt;p&gt;Selenium Grid, in short, is a central server that our test framework (Nightwatch.js) will be able to send requests to. Selenium Grid can spin up multiple Nodes which will allow for test execution against remote-controlled headless Chrome Instances.&lt;/p&gt;

&lt;p&gt;The beauty of Selenium Grid is that it is highly scalable. Each Node can have a defined amount of Chrome instances (or Firefox, Internet Explorer, and Opera). And a grid can register an infinite amount of Nodes.&lt;/p&gt;

&lt;p&gt;Nodes can either live on the same instance as the Selenium Grid Hub (the central processing endpoint), or the Nodes can live on separate server instances to allow for fine-tuned server scaling for each node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nightwatch.js
&lt;/h2&gt;

&lt;p&gt;We wanted to integrate Nightwatch.js into our end-to-end testing pipelines because it integrates with our existing microservices flawlessly.&lt;/p&gt;

&lt;p&gt;Tests are simple to write, with the comprehensive API developer documentation. And is fully supported by the community if we run into anything tricky.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cucumber
&lt;/h2&gt;

&lt;p&gt;Cucumber allows us to write tests in a human-readable, user story kind of manner. This works great for our inhouse testers as it can be tricky for them to write tests from a technical mindset. Having the tests as user stories already integrate with our current working methods. This also helps us as a team to think about test scenarios from the perspective of a user.&lt;/p&gt;

&lt;p&gt;Here's an example of a Cucumber test:&lt;br&gt;
&lt;/p&gt;

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

    Scenario: Google webpage loads

        Given I navigate to Google
        And I log in with a valid-user
        And I use the search filter to search for "Test Automation"
        Then the title is "Google - Test Automation"

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

&lt;/div&gt;



&lt;p&gt;Cucumber will 'translate' this into a full test and pass it over to Nightwatch, which will execute the tests.&lt;/p&gt;

&lt;h1&gt;
  
  
  Putting it all together
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Setting up Cucumber to use Nightwatch.js
&lt;/h2&gt;

&lt;p&gt;This was tricky at first as Nightwatch.js is a standalone test runner, it works without Cucumber. Tests can be written by only using Nightwatch.js if BDD isn't needed. But we required Cucumber as we wanted to involve our testers as much as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the necessary npm packages
&lt;/h3&gt;

&lt;p&gt;To be able to run Cucumber with Nightwatch.js, we need some packages from &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cucumber&lt;/li&gt;
&lt;li&gt;cucumber-pretty&lt;/li&gt;
&lt;li&gt;nightwatch&lt;/li&gt;
&lt;li&gt;nightwatch-api&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These can be installed by running the following command (global install)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo npm i -g cucumber cucumber-pretty nightwatch nightwatch-api

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up your node environment to execute end-to-end tests
&lt;/h3&gt;

&lt;p&gt;Next, we need to add a build script to our &lt;code&gt;package.json&lt;/code&gt; to allow us to run Cucumber from npm&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// package.json
"scripts": {
        ...
        "e2e-test": "cucumber-js --require cucumber.conf.js --require tests/**/step-definitions --format node_modules/cucumber-pretty tests/*"
    },

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

&lt;/div&gt;



&lt;p&gt;We also need to create a &lt;code&gt;cucumber.conf.js&lt;/code&gt; file to specify our Cucumber settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// cucumber.conf.js
const { setDefaultTimeout, AfterAll, BeforeAll } = require('cucumber');
const { createSession, closeSession } = require('nightwatch-api');

setDefaultTimeout(60000);

BeforeAll(async () =&amp;gt; {
  await createSession();
});

AfterAll(async () =&amp;gt; {
  await closeSession();
});

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

&lt;/div&gt;



&lt;p&gt;Finally, we need to create a &lt;code&gt;nightwatch.conf.js&lt;/code&gt; file to add our execution settings&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// nightwatch.conf.js
module.exports = {
    "src_folders" : ["tests/"],

    "selenium" : {
        "start_process" : false, // Important for running test execution on a remote Selenium Grid
        "host" : "seleniumgrid.example.com", // Endpoint for Selenium Grid
        "port" : 443,
        "use_ssl": true
    },

    "test_settings" : {
        "default" : {
            "selenium_port": 443,
            "selenium_host": "seleniumgrid.example.com", // Endpoint for Selenium Grid
            "use_ssl": true,
            "desiredCapabilities": {
                "browserName": "chrome", // Specified Browser
                "javascriptEnabled": true,
                "acceptSslCerts": true,
                "chromeOptions": {
                    "w3c": false, // Important
                    "args": ["--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu", "--disable-dev-shm-usage"], // Important
                },
                // This is to pass config when using Nodes
                "goog:chromeOptions": {
                    "w3c": false, // Important
                    "args": ["--headless", "--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu", "--disable-dev-shm-usage"], // Important
                }
                // Both chromeOptions objects are required for this to work
            },
        },
    }
}

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

&lt;/div&gt;



&lt;p&gt;That's all the configuration needed, your folder structure should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project_root/
  package.json
  cucumber.conf.js
  nightwatch.conf.js

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Folder Structure
&lt;/h3&gt;

&lt;p&gt;Cucumber is very particular about folder structure. The recommended folder structure is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project_root
├ cucumber.conf.js
├ nightwatch.conf.js
├ package.json
└ tests
    └ features
        ├ ebay.feature
        ├ google.feature
        └ step-definitions
            ├ ebay.js
            ├ google.js
            └ shared.js

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provisioning Selenium Grid
&lt;/h2&gt;

&lt;p&gt;This is where we create the Selenium Grid server. We host ours in AWS EC2 on a T2.micro Ubuntu Server instance. We tested with CentOS but could not get everything working properly. Ubuntu Server works flawlessly and is highly recommended if you're looking at setting this up.&lt;/p&gt;

&lt;p&gt;Here is a script that I wrote to provision Selenium Grid on Ubuntu Server. It can copy this script and save it as a &lt;code&gt;.sh&lt;/code&gt; file. Then execute this file in the scope of your Linux instance. It will require some tweaking of the paths to match up with your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#! /bin/bash

# This script requires default-jre and unzip to be installed
# Run the following as Sudo
# `sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y default-jre unzip xvfb screen`

# Enable execute permissions with
# `sudo chmod +x setup.sh`

# Please execute this script as your user
# e.g `sudo -u your_user ./setup.sh`

cd $HOME

mkdir TestAutomation
cd TestAutomation

# Download Chrome
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb

# Install Chrome
sudo apt install -y ./google-chrome-stable_current_amd64.deb

# Remove deb file
rm google-chrome-stable_current_amd64.deb

# Download Selenium Grid
wget https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar

# Download Chrome Driver
wget https://chromedriver.storage.googleapis.com/83.0.4103.39/chromedriver_linux64.zip

unzip chromedriver_linux64.zip

rm chromedriver_linux64.zip

# Create spawn files
export DISPLAY=:0.0
echo "screen -d -m java -jar /home/ubuntu/TestAutomation/selenium-server-standalone-3.141.59.jar -role hub" &amp;gt; ../spawn-hub.sh
echo "screen -d -m xvfb-run --auto-servernum --server-num=0 java -Dwebdriver.chrome.driver=/home/ubuntu/TestAutomation/chromedriver -Dwebdriver.chrome.bin=/usr/bin/google-chrome -jar /home/ubuntu/TestAutomation/selenium-server-standalone-3.141.59.jar -role node -hub http://localhost:4444/grid/register -browser browserName=chrome,maxInstances=10" &amp;gt; ../spawn-node.sh

# Set spawn file permissions
chmod +x ../spawn-*

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

&lt;/div&gt;



&lt;p&gt;Executing the generated &lt;code&gt;spawn-hub.sh&lt;/code&gt; will result in Selenium Grid spinning up on the port &lt;code&gt;4444&lt;/code&gt;. The grid will then listen for Nodes.&lt;/p&gt;

&lt;p&gt;To spin up a Node, execute the generated script &lt;code&gt;spawn-node.sh&lt;/code&gt;. This command can be executed multiple times to spin up multiple Nodes.&lt;/p&gt;

&lt;p&gt;TIP: If you want your Nodes on separate instances, copy the above provisioning script and execute it on your Node instances. But only execute the &lt;code&gt;spawn-node.sh&lt;/code&gt; script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tying it together
&lt;/h2&gt;

&lt;p&gt;Now all that is needed is to expose your Selenium Grid server to the web. I won't go into detail on how to do this, as it's different for everyone.&lt;/p&gt;

&lt;p&gt;Make sure that you update your &lt;code&gt;nightwatch.conf.js&lt;/code&gt; file to point to your new hosted endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Executing the tests
&lt;/h2&gt;

&lt;p&gt;All that is now needed to run these tests is the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run e2e-test

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

&lt;/div&gt;



&lt;p&gt;To recap...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This will run the Cucumber command to find all cucumber tests and their respective step-definitions.&lt;/li&gt;
&lt;li&gt;Pass them to Nightwatch to act as the test handler.&lt;/li&gt;
&lt;li&gt;Nightwatch will pass those tests to our remote Selenium Grid instance.&lt;/li&gt;
&lt;li&gt;Selenium Grid will distribute the tests to the test Nodes.&lt;/li&gt;
&lt;li&gt;The test Nodes will spin up Google Chrome and execute their assigned tests.&lt;/li&gt;
&lt;li&gt;Once finished, passing back the test output to Nightwatch.js and displaying the output in the npm console.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Chrome Plugins
&lt;/h2&gt;

&lt;p&gt;It's also worth mentioning that this execution pipeline allows for a full instance of Google Chrome to run, which means that you can use browser plugins as part of your execution pipeline. This is a rare thing to achieve but is possible using the above steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding all this to Buddy
&lt;/h2&gt;

&lt;p&gt;Adding to Buddy was relatively simple. We created a small pipeline to pull down the latest change from this test repository.&lt;/p&gt;

&lt;p&gt;Buddy allows an action to call another pipeline. So as part of our microservice pipeline, we call the external tests pipeline to execute the tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- action: "Run Selenium Grid Tests"
    type: "RUN_NEXT_PIPELINE"
    wait: true
    comment: "Triggered by $BUDDY_PIPELINE_NAME execution #$BUDDY_EXECUTION_ID"
    trigger_condition: "ALWAYS"
    revision: "HEAD"
    variables:
    - key: "TEST_TAGS"
      value: "\"@google\""
    next_project_name: "automationtests"
    next_pipeline_name: "Run Selenium Grid Tests"

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

&lt;/div&gt;



&lt;p&gt;We used Tags to be able to execute specific tests as part of our deployment, that's the &lt;code&gt;TEST_TAGS&lt;/code&gt; variable you can see in the above snippet.&lt;/p&gt;

&lt;p&gt;Here's our pipeline for running the test project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- pipeline: "Run Selenium Grid Tests"
  trigger_mode: "MANUAL"
  ref_name: "master"
  ref_type: "BRANCH"
  trigger_condition: "ALWAYS"
  actions:
  - action: "Format Test Tags"
    type: "BUILD"
    working_directory: "/buddy/automationtests"
    docker_image_name: "library/ubuntu"
    docker_image_tag: "18.04"
    execute_commands:
    - "TAGS=$TEST_TAGS"
    - "NODE_ENV=$ENV"
    volume_mappings:
    - "/:/buddy/automationtests"
    trigger_condition: "ALWAYS"
    shell: "BASH"
  - action: "Find &amp;amp; replace"
    type: "REPLACE"
    local_path: "package.json"
    replacements:
    - replace_from: "{TAGS}"
      replace_to: "$TAGS"
    trigger_condition: "ALWAYS"
  - action: "Run tests with Selenium Grid"
    type: "BUILD"
    working_directory: "/buddy/automationtests"
    docker_image_name: "library/node"
    docker_image_tag: "12"
    execute_commands:
    - "# yarn install"
    - "npm install"
    - ""
    - "npm run e2e-test-buddy --silent -- tests/* &amp;gt;&amp;gt; test-output.txt"
    volume_mappings:
    - "/:/buddy/automationtests"
    trigger_condition: "ALWAYS"
    shell: "BASH"
    execute_every_command: true
  variables:
  - key: "NODE_ENV"
    settable: true

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  That's it
&lt;/h2&gt;

&lt;p&gt;All done, you should now have remote test execution.&lt;/p&gt;

&lt;p&gt;Please get in touch with us if you have any issues&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My Self Hosted Note Syncing Journey Once Switching to Iphone</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Tue, 25 Feb 2020 11:32:00 +0000</pubDate>
      <link>https://dev.to/edleeman/my-self-hosted-note-syncing-journey-once-switching-to-iphone-1bo1</link>
      <guid>https://dev.to/edleeman/my-self-hosted-note-syncing-journey-once-switching-to-iphone-1bo1</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lv2Hnvba--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1483546416237-76fd26bbcdd1%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--lv2Hnvba--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1483546416237-76fd26bbcdd1%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="My Self Hosted Note Syncing Journey Once Switching to Iphone"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently moving to the iPhone meant that I had to re-think and re-design my existing note-taking solution, this is a quick writeup of my journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  My existing solution with Syncthing and VS Code
&lt;/h2&gt;

&lt;p&gt;I &lt;strong&gt;loved&lt;/strong&gt; my original solution. I had recently discovered &lt;a href="https://syncthing.net/"&gt;Syncthing&lt;/a&gt; and got it set up on my self hosted solution in my loft. I had a central sync server and then a node on each of my devices, including my Huawei P20 Pro.&lt;/p&gt;

&lt;p&gt;I could keep my extensive markdown nodes on every device and sync them without even thinking about it. It just worked, I wasn't tied into a service as the nodes are written in Markdown.&lt;/p&gt;

&lt;p&gt;Everything just &lt;strong&gt;worked&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switching to iPhone
&lt;/h2&gt;

&lt;p&gt;I made the switch to the iPhone due to privacy concerns with Android. I have recently boycotted all my Google services and switched over to a self-hosted solution. Part of this meant getting rid of my Android.&lt;/p&gt;

&lt;p&gt;Everything started great with the iPhone, the interface is great, everything just works.&lt;/p&gt;

&lt;p&gt;Apart from Syncthing.&lt;/p&gt;

&lt;p&gt;There's currently no iOS client for Syncthing.&lt;/p&gt;

&lt;p&gt;So what now? I started looking at other app alternatives, Bear, Markdown Notes, etc.. but all of them locked down where the files were saved on the iPhone, I couldn't access them from a file explorer on the iPhone. Something that could easily be done with Android.&lt;/p&gt;

&lt;p&gt;Bumped into &lt;a href="https://www.resilio.com/"&gt;Resilio&lt;/a&gt;, not self-hosted, but achieves the whole decentralized syncing that I loved about Syncthing. Cool, I managed to get my notes syncing to a Resilio folder on my iPhone. Bingo!&lt;/p&gt;

&lt;p&gt;But wait, what happens when I try and edit a note? It opens a &lt;em&gt;copy&lt;/em&gt; of the note in a markdown app. Not ideal, I just want to view and edit notes, how hard can it be!&lt;/p&gt;

&lt;p&gt;I give up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standard Notes
&lt;/h2&gt;

&lt;p&gt;I &lt;strong&gt;had&lt;/strong&gt; a love-hate relationship with &lt;a href="https://standardnotes.org/"&gt;Standard Notes&lt;/a&gt;. I loved the UI, the syncing was pretty good, it has an iOS app! and it can be self-hosted, amazing.&lt;/p&gt;

&lt;p&gt;But a lot of the features are paid. So if I wanted to write in Markdown, which I do, I had to pay for the extensions. I had to think to myself, how badly do I want notes on my iPhone?&lt;/p&gt;

&lt;p&gt;I went without notes for a week, it nearly killed me. So I bit the bullet and settled for one month at £9.99, I thought I'd give it a try.&lt;/p&gt;

&lt;p&gt;Loved it, I heavily use Markdown Basic, Simple Task Editor and Secure Spreadsheets extensions. But can I justify £10 a month?&lt;/p&gt;

&lt;h2&gt;
  
  
  Standard Notes Extensions
&lt;/h2&gt;

&lt;p&gt;Standard Notes being open-source meant that I could have a poke around in its git repository. I did a global search on Github for "Standard Notes" and I bumped into a repository by Github User &lt;a href="https://github.com/jonhadfield/awesome-standard-notes"&gt;jonhadfield&lt;/a&gt; who has collected some awesome Standard Notes extensions. Browsing his &lt;code&gt;README.md&lt;/code&gt; I spotted the repository &lt;a href="https://github.com/iganeshk/standardnotes-extensions"&gt;Extensions Repository Builder&lt;/a&gt;. This would allow me to self-host my extensions rather than paying the monthly fee!&lt;/p&gt;

&lt;p&gt;I set up Extensions Repository Builder on my internal host and bingo, it just worked, amazing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full E2E Sync
&lt;/h2&gt;

&lt;p&gt;So after a very long journey of pulling my hair out and re-evaluating how much these notes meant to me. I finally had an end-to-end solution for storing my notes and syncthing between all of my devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I'm going next
&lt;/h2&gt;

&lt;p&gt;I have a &lt;a href="https://theptrk.com/2018/07/11/did-txt-file/"&gt;did.txt&lt;/a&gt; file which I log to every day with what I did that day, it helps with our daily standups so that I can remember what I did the previous day. On &lt;a href="https://github.com/jonhadfield/awesome-standard-notes"&gt;jonhadfield's&lt;/a&gt; Github, there was also a link for &lt;a href="https://github.com/tannercollin/standardnotes-fs"&gt;standardnotes-fs&lt;/a&gt; which allows you to mount your Standard Notes as files using fuse. This means I can set up my did.txt command to edit my Did note.&lt;/p&gt;

&lt;p&gt;I'd also like to hook up iOS reminders to a reminders section in Standard Notes. I own an Apple Watch and I tend to use Siri on it quite often. So I need to work out some way to sync these with my standard notes.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Route Nginx Through a Child Nginx Configuration - Jumpbox to Web Server Configuration</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Thu, 02 Jan 2020 11:34:00 +0000</pubDate>
      <link>https://dev.to/edleeman/how-to-route-nginx-through-a-child-nginx-configuration-jumpbox-to-web-server-configuration-5077</link>
      <guid>https://dev.to/edleeman/how-to-route-nginx-through-a-child-nginx-configuration-jumpbox-to-web-server-configuration-5077</guid>
      <description>&lt;p&gt;I had a specific use case where I needed to run a docker instance, which had it's own configured nginx instance.&lt;/p&gt;

&lt;p&gt;The issue was that I already use nginx on my JumpBox.&lt;/p&gt;

&lt;p&gt;The fix was relatively self explanatory, but I wanted somewhere to write down the issues that I had to tackle.&lt;/p&gt;

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

&lt;p&gt;So the solution is just to point nginx (JumpBox) to the other nginx instance (docker).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Just to let you know, in my case, there are separate physical servers. The configuration will not differ, but you may need to internally expose ports between hosts if you have something like UFW installed. &lt;a href="https://blog.bowlerdesign.tech/2019/12/15/setting-up-ufw-on-ubuntu-server/"&gt;I have a tutorial here on how to set up UFW.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So on my Host &lt;code&gt;B&lt;/code&gt; I have a web instance running on port &lt;code&gt;8080&lt;/code&gt;. This web instance is an nginx reverse proxy that is pointing to another docker instance within the same docker network on the machine.&lt;/p&gt;

&lt;p&gt;Host &lt;code&gt;A&lt;/code&gt; is my existing nginx reverse proxy.&lt;/p&gt;

&lt;p&gt;The quick solution is to forward your call from Host &lt;code&gt;A&lt;/code&gt; to Host &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
  server_name blog.bowlerdesign.tech;

  location / {
    proxy_pass http://host_running_blog:8080;
  }

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

&lt;/div&gt;



&lt;p&gt;I ran into issues here, I couldn't get css loading properly when navigating to blog.bowlerdesign.tech.&lt;/p&gt;

&lt;p&gt;I was missing headers in the request. Here's an ideal configuration for setting up a website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
  server_name blog.bowlerdesign.tech;

  location / {
    proxy_pass http://host_running_blog:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto scheme;
    proxy_pass_header Content-Type;
  }

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

&lt;/div&gt;



&lt;p&gt;This will forward the necessary headers for your host.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting Up UFW on Ubuntu Server</title>
      <dc:creator>bowlerdesign.tech</dc:creator>
      <pubDate>Sun, 15 Dec 2019 11:29:00 +0000</pubDate>
      <link>https://dev.to/edleeman/setting-up-ufw-on-ubuntu-server-4kok</link>
      <guid>https://dev.to/edleeman/setting-up-ufw-on-ubuntu-server-4kok</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="Setting Up UFW on Ubuntu Server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.ubuntu.com/community/UFW"&gt;UFW&lt;/a&gt; (Uncomplicated Firewall) is a program that allows you to internally control ports on your Linux instance. This gives you the ability to forward ports from your machine.&lt;/p&gt;

&lt;p&gt;The common use of a firewall is to control the ports that have access from the outside world, for instance, running a website would need ports &lt;code&gt;80&lt;/code&gt;/&lt;code&gt;443&lt;/code&gt; exposed on your network to be able to route your site.&lt;/p&gt;

&lt;p&gt;UFW is different, think of port forwarding, but between local instances. You can lock down internal exposure to port &lt;code&gt;22&lt;/code&gt; (&lt;code&gt;ssh&lt;/code&gt;) for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why? What's the point?
&lt;/h2&gt;

&lt;p&gt;Security.&lt;/p&gt;

&lt;p&gt;In the coming weeks, I'll be writing blog posts on how I have set up my jumpbox server. UFW plays a key part in my setup. I have used UFW to only open port 22 on my jumpbox server. I can &lt;code&gt;ssh&lt;/code&gt; in, but that's it. No other ports can be attacked or sniffed.&lt;/p&gt;

&lt;p&gt;From the jumpbox server I can then &lt;strong&gt;only&lt;/strong&gt; &lt;code&gt;ssh&lt;/code&gt; into my other internal instances. This means that if I wanted to &lt;code&gt;ssh&lt;/code&gt; into server &lt;code&gt;B&lt;/code&gt; I would have to go via the jumpbox &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  I think I get it, how can I install it?
&lt;/h2&gt;

&lt;p&gt;We're running &lt;a href="https://ubuntu.com/download/raspberry-pi"&gt;Ubuntu Server on a Raspberry Pi&lt;/a&gt;. But these instructions are for all Debian instances, the Raspberry Pi is irrelevant for this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's install
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Install UFW&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo apt-get install ufw&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the status of UFW&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo ufw status&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see that UFW is &lt;strong&gt;disabled&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let's allow some ports, it's really important that you allow your &lt;code&gt;ssh&lt;/code&gt; port, otherwise you can lose access when we get round to enabling UFW.
The default &lt;code&gt;ssh&lt;/code&gt; port is &lt;strong&gt;22&lt;/strong&gt; unless you have changed the default port.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo ufw allow 22&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can use the above command to allow the necessary ports for your instance. We're just going to stick with port &lt;code&gt;22&lt;/code&gt; for this example.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let's enable UFW&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo ufw enable&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it
&lt;/h2&gt;

&lt;p&gt;You now have a firewall running on your local instance, locked down to be only accessible by port &lt;code&gt;22&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the future, if you're running services on this box, you'll need to expose any other ports that you want to have access outside of your machine. Let's say you set up &lt;a href="https://openvpn.net/"&gt;OpenVPN&lt;/a&gt;, you have to expose port &lt;code&gt;1194&lt;/code&gt; on the machine it's running on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading
&lt;/h2&gt;

&lt;p&gt;Thanks for reading, hope I've helped in some way!&lt;/p&gt;

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