<?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: Zachary Becker</title>
    <description>The latest articles on DEV Community by Zachary Becker (@zrbecker).</description>
    <link>https://dev.to/zrbecker</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%2F94825%2F9448f0e1-191f-4126-80d6-e4e30fa66b1a.jpeg</url>
      <title>DEV Community: Zachary Becker</title>
      <link>https://dev.to/zrbecker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zrbecker"/>
    <language>en</language>
    <item>
      <title>Querying CSV Files Using Postgres</title>
      <dc:creator>Zachary Becker</dc:creator>
      <pubDate>Mon, 04 Sep 2023 04:54:33 +0000</pubDate>
      <link>https://dev.to/zrbecker/querying-csv-files-using-postgres-4idg</link>
      <guid>https://dev.to/zrbecker/querying-csv-files-using-postgres-4idg</guid>
      <description>&lt;p&gt;While reading &lt;a href="https://www.amazon.com/PostgreSQL-Running-Practical-Advanced-Database/dp/1491963417"&gt;PostgreSQL: Up and Running&lt;/a&gt;, I noticed a few features where it is easy to import data from a CSV file into postgres. However, the example in the book required a complex &lt;code&gt;CREATE TABLE&lt;/code&gt; query that was specific to the CSV file that they were loading.&lt;/p&gt;

&lt;p&gt;I figured it would be cool if we could load an arbitrary CSV files, and automatically create a table based on the first line of the CSV file which often contains the column names for the data included in the file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sample Dataset
&lt;/h1&gt;

&lt;p&gt;To test this out, I used a free data set (&lt;a href="https://catalog.data.gov/dataset/electric-vehicle-population-data"&gt;Electric Vehicle Population Data&lt;/a&gt;) from the State of Washington which contains data about registered electric vehicles in the state.&lt;/p&gt;

&lt;p&gt;You can easily grab this data set with the following shell command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="se"&gt;\&lt;/span&gt;
  https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType&lt;span class="o"&gt;=&lt;/span&gt;DOWNLOAD &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-O&lt;/span&gt; Electric_Vehicle_Population_Data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Load Column Data
&lt;/h1&gt;

&lt;p&gt;This CSV file contains the expected header row&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIN (1-10),County,City,State,Postal Code,Model Year,Make,Model,Electric Vehicle Type,Clean Alternative Fuel Vehicle (CAFV) Eligibility,Electric Range,Base MSRP,Legislative District,DOL Vehicle ID,Vehicle Location,Electric Utility,2020 Census Tract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to generate a &lt;code&gt;CREATE TABLE&lt;/code&gt; command to create a table based on these columns, we first need to load the column names into PostgreSQL.&lt;/p&gt;

&lt;p&gt;However, since we don't care about persisting this information past the task of creating the table of electric vehicle data, we can load it into a temporary table.&lt;/p&gt;

&lt;p&gt;From a &lt;code&gt;psql&lt;/code&gt; prompt, I created this temporary table and loaded it with data with the following commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TEMP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"csv_columns"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;"name"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="nv"&gt;"csv_columns"&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;PROGRAM&lt;/span&gt; &lt;span class="s1"&gt;'head -n 1 Electric_Vehicle_Population_Data.csv | sed &lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;s/,/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;/g&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command reads the first line of our csv file, and replaces the commands with newlines. The copy command takes each line of our output, and puts it into our &lt;code&gt;csv_columns&lt;/code&gt; table.&lt;/p&gt;

&lt;h1&gt;
  
  
  Loading CSV Data
&lt;/h1&gt;

&lt;p&gt;With psql, we can construct a query with a &lt;code&gt;SELECT&lt;/code&gt; statement, based on our columns table, and execute that query with the &lt;code&gt;\gexec&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;We employ the built in functions &lt;code&gt;array_to_string&lt;/code&gt; which is like &lt;code&gt;strings.Join&lt;/code&gt; in main languages, and &lt;code&gt;array_agg&lt;/code&gt; which aggregates values into a single value.&lt;/p&gt;

&lt;p&gt;We can create the table based on the loaded column information using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="s1"&gt;'CREATE TABLE "public"."electric_vehicles" ('&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="n"&gt;array_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'" TEXT'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;');'&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"csv_columns"&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;gexec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This effectively executes the SQL query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"electric_vehicles"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;"VIN (1-10)"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"County"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"City"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"State"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Postal Code"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nv"&gt;"Model Year"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Make"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Model"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Electric Vehicle Type"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Clean Alternative Fuel Vehicle (CAFV) Eligibility"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Electric Range"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Base MSRP"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Legislative District"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"DOL Vehicle ID"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Vehicle Location"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"Electric Utility"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"2020 Census Tract"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have our table, we can load the CSV data with the copy command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"electric_vehicles"&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'Electric_Vehicle_Population_Data.csv'&lt;/span&gt; &lt;span class="n"&gt;CSV&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Querying CSV Data in PostgreSQL
&lt;/h1&gt;

&lt;p&gt;We can now query our CSV data as a SQL table, for example, this gets the top 5 makes of electric vehicles registered in Washington State.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="nv"&gt;"Make"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nv"&gt;"Count"&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"electric_vehicles"&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nv"&gt;"Make"&lt;/span&gt; 
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nv"&gt;"Count"&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which outputs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Make    | Count
-----------+-------
 TESLA     | 65552
 NISSAN    | 13317
 CHEVROLET | 11816
 FORD      |  7307
 BMW       |  6209
(5 rows)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;I found this a really interesting way to learn PostgreSQL better, and perhaps a way I can grok CSV data in the future.&lt;/p&gt;

&lt;h1&gt;
  
  
  Code
&lt;/h1&gt;

&lt;p&gt;Here is a full &lt;code&gt;psql&lt;/code&gt; script to load and query the Electric Vehicle Population Data CSV file. I named it &lt;code&gt;create_ev_dataset.sql&lt;/code&gt;, and execute it with &lt;code&gt;psql -f create_ev_dataset.sql&lt;/code&gt;. It assumes the CSV is in the directory that you ran &lt;code&gt;psql&lt;/code&gt; from.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- create a temporary table to load the column names into&lt;/span&gt;
&lt;span class="c1"&gt;-- this table only exists for the duration of the connection&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TEMP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"csv_columns"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;"name"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- split the first line of the csv file to get the column names on individual lines, and copy them into our temporary table&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="nv"&gt;"csv_columns"&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;PROGRAM&lt;/span&gt; &lt;span class="s1"&gt;'head -n 1 Electric_Vehicle_Population_Data.csv | sed &lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;s/,/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;/g&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;-- use our temporary table of column names to construct a table to store csv data in&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="s1"&gt;'CREATE TABLE "public"."electric_vehicles" ('&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="n"&gt;array_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'" TEXT'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;');'&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"csv_columns"&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;gexec&lt;/span&gt;

&lt;span class="c1"&gt;-- copy csv data into the table&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"electric_vehicles"&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'Electric_Vehicle_Population_Data.csv'&lt;/span&gt; &lt;span class="n"&gt;CSV&lt;/span&gt;

&lt;span class="c1"&gt;-- query csv data using postgres&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="nv"&gt;"Make"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nv"&gt;"Count"&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"electric_vehicles"&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nv"&gt;"Make"&lt;/span&gt; 
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nv"&gt;"Count"&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



</description>
      <category>postgres</category>
    </item>
    <item>
      <title>A Minimal Node.js and Express Server Setup and Deployment</title>
      <dc:creator>Zachary Becker</dc:creator>
      <pubDate>Thu, 23 Aug 2018 03:04:21 +0000</pubDate>
      <link>https://dev.to/zrbecker/a-minimal-nodejs-and-express-server-setup-and-deployment-3noe</link>
      <guid>https://dev.to/zrbecker/a-minimal-nodejs-and-express-server-setup-and-deployment-3noe</guid>
      <description>&lt;p&gt;As a software engineer I often forget the complexities of how my code is deployed. When I run the code I write, typically it is done on my local development environment, and then I push my code to a repository where through continuous deployment magic my code becomes available to my end users.&lt;/p&gt;

&lt;p&gt;While I reap many benefits from this complexity, I often forget why it is there, and when things don't work, I am simply annoyed by the complexity.&lt;/p&gt;

&lt;p&gt;In this article I put together a very minimal Node.js and Express application and deploy it manually onto an AWS instance. The goal of this exercise is to evaluate the advantages and disadvantages to doing my deployment like this.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: It's worth mentioning by minimal I mean fewest number of pieces, not necessarily fewest number of steps. Something like Heroku will likely get you up and running much quicker.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The Application
&lt;/h1&gt;

&lt;p&gt;The application I have in mind is a simple "Hello World!" Express application. First I run a few commands in bash to set up my envrionment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;express_hello_world
&lt;span class="nb"&gt;cd &lt;/span&gt;express_hello_world
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I create &lt;code&gt;server.js&lt;/code&gt; with the following JavaScript code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if I run my code with &lt;code&gt;node server.js&lt;/code&gt;, I should be able to go to &lt;a href="http://localhost:3000/"&gt;http://localhost:3000/&lt;/a&gt; and see the message "Hello World" in my browser.&lt;/p&gt;

&lt;p&gt;Great! Now commit and push, and move onto the next thing right? Oh wait, we are deploying manually, we got some work to do.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Server
&lt;/h1&gt;

&lt;p&gt;We need a computer of some sort to run our code on the web. Especially since my laptop isn't online 24/7, and we wouldn't want anyone to not be able to access our greeting. I head to the &lt;a href="https://console.aws.amazon.com/"&gt;AWS console&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Click on "EC2"&lt;/li&gt;
&lt;li&gt;  Click on "Launch Server"&lt;/li&gt;
&lt;li&gt;  Select "Amazon Linux 2 AMI"&lt;/li&gt;
&lt;li&gt;  Click "Review and Launch"&lt;/li&gt;
&lt;li&gt;  Click "Launch"&lt;/li&gt;
&lt;li&gt;  In dropdown select "Create a new key pair"&lt;/li&gt;
&lt;li&gt;  Enter "express_hello_world" as my "Key pair name"&lt;/li&gt;
&lt;li&gt;  Click "Download Key Pair"&lt;/li&gt;
&lt;li&gt;  Click "Launch Instances"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whew! That was a few steps, most of which I didn't really take the time to evaluate the best options, but now we have some sort of "default" server to run our code on. However, we still need to install Node.js on the server, and upload our "Hello World" application to the server, and then make sure it is running.&lt;/p&gt;

&lt;h1&gt;
  
  
  Connecting to the Server
&lt;/h1&gt;

&lt;p&gt;The key pair we downloaded was a "pem" file which is a private key. We can use this key to connect to the server via an SSH client. We also need the public IP address of our AWS server we created. From the "EC2 Dashboard" click "Instances" on the left sidebar, and find the column "IPv4 Public IP" which has the IP adress for the instance we just created.&lt;/p&gt;

&lt;p&gt;Now we can connect to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/Documents/AWS/keys/express_hello_world/
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Documents/AWS/keys/express_hello_world/
&lt;span class="nb"&gt;cp&lt;/span&gt; ~/Downloads/express_hello_world.pem ./
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 express_hello_world.pem
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"express_hello_world.pem"&lt;/span&gt; ec2-user@&amp;lt;PUBLIC_IP_ADDRESS_OF_INSTANCE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now be at a bash prompt on your AWS EC2 instance.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If you are using Windows, you should probably follow the instructions for &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/putty.html?icmpid=docs_ec2_console"&gt;Connecting to Your Linux Instance from Windows Using PuTTY&lt;/a&gt;. However, you can use "Windows Subsystem from Linux" (WSL). If you use WSL, make sure you put &lt;code&gt;express_hello_world.pem&lt;/code&gt; in a WSL folder or &lt;code&gt;chmod 400 express_hello_world.pem&lt;/code&gt; will not work.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Installing Node.js
&lt;/h1&gt;

&lt;p&gt;From the Node.js home page the installation instructions for the yum package manager which is what our server instance using are as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nt"&gt;--location&lt;/span&gt; https://rpm.nodesource.com/setup_8.x | &lt;span class="nb"&gt;sudo &lt;/span&gt;bash -
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can type &lt;code&gt;node --version&lt;/code&gt; to check if it is installed and you are on the correct version. The current LTS version was 8.11.4 when this article was written. Anything greater than that should work fine.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating a User
&lt;/h1&gt;

&lt;p&gt;We will create a user for running our application. This prevents us from having to worry about what our application can do as root, or even potentially with sudo abilities as &lt;code&gt;ec2-user&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd express_hello_world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Copying Application to Server
&lt;/h1&gt;

&lt;p&gt;Exit from our current ssh session by typing &lt;code&gt;exit&lt;/code&gt;. Find the directory where we originally put our &lt;code&gt;server.js&lt;/code&gt; and &lt;code&gt;package.json&lt;/code&gt; files. We will copy that entire directory to the &lt;code&gt;ec2-user&lt;/code&gt; home directory on our EC2 server instance.&lt;/p&gt;

&lt;p&gt;After we accomplish that, we want to set the permissions so the owner is the &lt;code&gt;express_hello_world&lt;/code&gt; user we just created, and move the application to the &lt;code&gt;express_helo_world&lt;/code&gt; home directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &amp;lt;EXPRESS_APP_DIRECTORY&amp;gt;/node_modules
scp &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"express_hello_world.pem"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &amp;lt;EXPRESS_APP_DIRECTORY&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    ec2-user@&amp;lt;PUBLIC_IP_ADDRESS_OF_INSTANCE&amp;gt;:express_hello_world_app
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"express_hello_world.pem"&lt;/span&gt; ec2-user@&amp;lt;PUBLIC_IP_ADDRESS_OF_INSTANCE&amp;gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 ./express_hello_world_app/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; express_hello_world:express_hello_world ./express_hello_world_app
&lt;span class="nb"&gt;sudo mv&lt;/span&gt; ./express_hello_world_app /home/express_hello_world/express_hello_world_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: For Windows users it probably makes the most sense to use a program like &lt;a href="https://filezilla-project.org/"&gt;FileZilla&lt;/a&gt; to copy the files to the server. Although with WSL you can use the &lt;code&gt;scp&lt;/code&gt; method described here. Again make sure your private key is in a WSL directory with the correct permissions (400).&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Checkpoint 1: Testing the Application Locally
&lt;/h1&gt;

&lt;p&gt;It is a good time to do a sanity check. We should be able to run the application from the &lt;code&gt;express_hello_world&lt;/code&gt; user, and use &lt;code&gt;curl&lt;/code&gt; to get our "Hello World" message.&lt;/p&gt;

&lt;p&gt;Become the &lt;code&gt;express_hello_world&lt;/code&gt; user and install Express dependency. We also test here to make sure we get a "Connection Refused" since the server should not be running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su - express_hello_world
&lt;span class="nb"&gt;cd&lt;/span&gt; ./express_hello_world_app
npm &lt;span class="nb"&gt;install
&lt;/span&gt;curl localhost:3000  &lt;span class="c"&gt;# Expected output "Connection Refused"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we should be able to start the server and get the "Hello World" response from it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node server.js &amp;amp;
&lt;span class="nv"&gt;SERVER_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;
curl localhost:3000  &lt;span class="c"&gt;# Expected output "Hello World"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we verified we get our "Hello World" response. Kill the server, and check that we are no longer accepting connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nv"&gt;$SERVER_PID&lt;/span&gt;
curl localhost:3000  &lt;span class="c"&gt;# Expected output "Connection Refused"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally type &lt;code&gt;exit&lt;/code&gt; to return to the &lt;code&gt;ec2-user&lt;/code&gt; user.&lt;/p&gt;

&lt;h1&gt;
  
  
  Exposing the Application to the World
&lt;/h1&gt;

&lt;p&gt;Since we are running our application as a unprivledged user, we cannot host it on port 80 (or any port below 1024). However, we can create a firewall rule to forward that port to the port we are hosting on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-j&lt;/span&gt; REDIRECT &lt;span class="nt"&gt;--to&lt;/span&gt; 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to setup our AWS security rules to allow traffic on port 80. From the EC2 Dashboard. Click on Instaces in the left sidebar. Right click on the instance we created, go to "Networking", then "Change Security Groups". Look at the "Security Group ID" we currently have selected, and remember it. Click "Cancel".&lt;/p&gt;

&lt;p&gt;On the left sidebar, click "Security Groups". Right click on the row with the "Security Group ID" we found in the last step. And click "Edit inbound rules". Click "Add Rule" and from the drop down, select "HTTP". Then click "Save".&lt;/p&gt;

&lt;h1&gt;
  
  
  Checkpoint 2: Testing the Application from the Web
&lt;/h1&gt;

&lt;p&gt;In your browser try going to &lt;a href=""&gt;http://&amp;lt;PUBLIC_IP_ADDRESS_OF_INSTANCE&amp;gt;&lt;/a&gt;. You should get a "This site can't be reached" message. This is because we stopped running the server in the previous step.&lt;/p&gt;

&lt;p&gt;Go ahead and launch the server again. Run the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su - express_hello_world
&lt;span class="nb"&gt;cd&lt;/span&gt; ./express_hello_world_app
node server.js &amp;amp;
&lt;span class="nv"&gt;SERVER_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we go to the previous URL, we should get a "Hello World" message in the browser.&lt;/p&gt;

&lt;p&gt;Awesome! We are now live!&lt;/p&gt;

&lt;p&gt;To shutdown the server we can kill the application like we did last time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nv"&gt;$SERVER_PID&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt;  &lt;span class="c"&gt;# To exit the express_hello_world user prompt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Running the Server as a Daemon Process
&lt;/h1&gt;

&lt;p&gt;We have a working server, but it would be much better if we didn't have the server process tied to our current SSH session. We can use the systemd process for doing this. It will also handle making sure the server is restarted if it fails, and automatically started when the server boots up.&lt;/p&gt;

&lt;p&gt;Create a file &lt;code&gt;express_hello_world.service&lt;/code&gt; in &lt;code&gt;/etc/systemd/system/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/systemd/system/express_hello_world.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to that file. These are the settings for our daemon process. You can control things like the command we use to start the process with "ExecStart". the User/Group, what environment variables are set, and the current Working Directory.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Express Hello World Application
After=network.target

[Service]
ExecStart=/usr/bin/node server.js
Restart=always
User=express_hello_world
Group=express_hello_world
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/home/express_hello_world/express_hello_world_app

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To start our job, we use &lt;code&gt;systemctl&lt;/code&gt;. We can use the status subcommand to check that status of our daemon process too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start express_hello_world.service
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status express_hello_world.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Debugging
&lt;/h2&gt;

&lt;p&gt;After running status, we should see "Active: active (running)". If you don't see that, something obviously went wrong. Use &lt;code&gt;sudo journalctl --unit express_hello_world.service&lt;/code&gt; to examine the error log of the &lt;code&gt;node service.js&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;One likely cause of the job failing is port 3000 is in use. This can happen when you forgot to kill the server in one of the checkpoints (This happened to me, haha). Do &lt;code&gt;ps aux | grep node&lt;/code&gt;, and find the process that still is running, note it's PID, then do &lt;code&gt;sudo kill &amp;lt;PID&amp;gt;&lt;/code&gt;. After this the daemon process should start without a problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rebooting
&lt;/h2&gt;

&lt;p&gt;Lastly to make sure the application starts when the server is booted, run the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;express_hello_world.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Persist iptables between reboots
&lt;/h3&gt;

&lt;p&gt;If you reboot now, the iptable rule that we defined before will not be restored on boot. We can also create a systemd service that does that. Create the file &lt;code&gt;/etc/systemd/service/express_hello_world_iptables.service&lt;/code&gt; and put the following systemd configuration.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Forward port 80 to port 3000

[Service]
Type=oneshot
ExecStart=/bin/sh -c "iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to 3000"
ExecStop=/bin/sh -c "iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to 3000"
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then delete the rule we already created and start the service we just defined (this recreates the rule).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-D&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-j&lt;/span&gt; REDIRECT &lt;span class="nt"&gt;--to&lt;/span&gt; 3000
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start express_hello_world_iptables.service
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;express_hello_world_iptables.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: It feels like there is a better way to handle this iptables stuff, but I couldn't find anything that felt very minimal. This doesn't seem like something that should be very complex.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Checkpoint 3: Testing our Daemon Process
&lt;/h1&gt;

&lt;p&gt;In your browser go to &lt;a href=""&gt;http://&amp;lt;PUBLIC_IP_ADDRESS_OF_INSTANCE&amp;gt;&lt;/a&gt; again. We should see the message "Hello World" if our last step succeeded.&lt;/p&gt;

&lt;p&gt;Run the following command to stop our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop express_hello_world.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And refresh the browser to make sure the server is no longer responding.&lt;/p&gt;

&lt;p&gt;Now reboot the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the server to come back up, and check the website. We should get the message "Hello World" again.&lt;/p&gt;

&lt;p&gt;Assuming everything went smoothly, we now have a functional server hosting our nodejs application!&lt;/p&gt;

&lt;h1&gt;
  
  
  Reflections
&lt;/h1&gt;

&lt;p&gt;This was a lot of steps to set everything up. However, most of it could easily be automated. Even provisioning the AWS service can be automated through the &lt;a href="https://aws.amazon.com/cli/"&gt;AWS Command Line Interface&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-Deploy
&lt;/h2&gt;

&lt;p&gt;To deploy a new version of our app, we would just need to copy the files to our server, and restart the daemon. Another step that should be fairly easy to automate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;The application runs exactly as we set it up. To change anything, we really have to know Linux very well. I can manage ok through Googling basic commands, but doing some simple things seem like that might be a pain, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Set up HTTPS (On Heroku this is a one click setup pretty much)&lt;/li&gt;
&lt;li&gt;  Move to another service. (With something like docker this might be easier, a lot of what I did I think was specific to Amazon Linux 2).&lt;/li&gt;
&lt;li&gt;  Scaling. (Something like docker would make it easy to run more instances of my app).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  iptables
&lt;/h2&gt;

&lt;p&gt;My understanding is that iptables is deprecated, but as a novice to Linux system administration, it is unclear to me what the available replacements are. A lot of the information I could find refered to commands that just didn't exist on my system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;I might try to write some scripts that automates this process to see how hard it is to deploy different apps on different servers. I certainly have some hard coded values in my systemd that I would have to deal with.&lt;/p&gt;

&lt;p&gt;All in all it was fun to get this up and running.&lt;/p&gt;

</description>
      <category>express</category>
      <category>aws</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
