<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Systemd on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/systemd/</link><description>Recent content in Systemd on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Tue, 22 Aug 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/systemd/index.xml" rel="self" type="application/rss+xml"/><item><title>Installing a Node app on a server</title><link>https://blog.iankulin.com/installing-a-node-app-on-a-server/</link><pubDate>Tue, 22 Aug 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/installing-a-node-app-on-a-server/</guid><description>&lt;p&gt;Before I write a fancy Ansible playbook to automatically set up the Nginx/Node combo on my web servers, it might be worth going through how to deploy a Node app so it can run on a server without you being logged in.&lt;/p&gt;
&lt;p&gt;Until now, I&amp;rsquo;ve been running my tests on my laptop, or in a server logged in as myself - sometimes detaching from tmux. But we need a bit more professional set up than that. The process will look something like this:&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/hqdefault.jpg" width="150" alt=""&gt;
&lt;ul&gt;
&lt;li&gt;Install Node and npm (I&amp;rsquo;m assuming we&amp;rsquo;ve done that since I&amp;rsquo;ve covered the playbook for it before).&lt;/li&gt;
&lt;li&gt;Copy the app files over&lt;/li&gt;
&lt;li&gt;Install the dependencies&lt;/li&gt;
&lt;li&gt;Write the systemd config file&lt;/li&gt;
&lt;li&gt;Start it up&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="app-files"&gt;App files&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ll use the very simple server (&lt;code&gt;index.js&lt;/code&gt;) I&amp;rsquo;ve written for the future Ansible post. All it does is listen on port 3000 to serve a tiny piece of text if someone hits the &lt;code&gt;/api&lt;/code&gt; route.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; express &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; app &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; express&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; PORT &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;3000&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;get&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;/api&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;200&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;Success - from /api route via node.js&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;listen&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;PORT&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;console&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;Listening on port &lt;span style="color:#81a1c1"&gt;$&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;PORT&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll also have our &lt;code&gt;package.json&lt;/code&gt;, of which the only interesting thing to notice is that we&amp;rsquo;ve got a dependency on the &lt;code&gt;express&lt;/code&gt; package which I originally installed with &lt;code&gt;npm&lt;/code&gt;. All the files for our dependencies are stored in the &lt;code&gt;./node_modules&lt;/code&gt; directory, but we don&amp;rsquo;t need to copy them to the server.&lt;/p&gt;
&lt;h3 id="where-to-put-the-app-files"&gt;Where to put the app files&lt;/h3&gt;
&lt;p&gt;If I was doing this for a commercial app, I might store the app under &lt;code&gt;/var/www/&amp;lt;app name&amp;gt;&lt;/code&gt; since that&amp;rsquo;s where a future sysadmin might look for it if they don&amp;rsquo;t have access to the playbooks. Another good place might be the home directory of the ansible/node user. Since that&amp;rsquo;s me in this case, they&amp;rsquo;re just going to go in my home directory - it makes the playbook commands shorter. We can use &lt;code&gt;scp&lt;/code&gt; to copy the files in.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-07-16-at-5.50.02-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Once the files are there, we can install the dependencies with &lt;code&gt;npm install&lt;/code&gt;. This looks at the &lt;code&gt;package.json&lt;/code&gt;, then grabs them down.&lt;/p&gt;
&lt;h3 id="systemd"&gt;systemd&lt;/h3&gt;
&lt;p&gt;systemd manages the init and daemon processes in most Linux distros, so we&amp;rsquo;ll be using that to get our node app running as a service. It was &lt;a href="https://en.wikipedia.org/wiki/Systemd#History"&gt;a present from Red Hat&lt;/a&gt;. Processes that run like this need a configuration file in &lt;code&gt;/lib/systemd/system&lt;/code&gt;, ours will be called&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/lib/systemd/system/test-server.service&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Unit]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Description=index.js - test server 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After=network.target
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Service]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Type=simple
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;User=ian 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ExecStart=/usr/bin/node /home/ian/index.js
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Restart=on-failure
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Install]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WantedBy=multi-user.target 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This file needs to say that we should wait for the network to come up before starting, what we&amp;rsquo;re running and what to do if it dies. &amp;lsquo;on-failure&amp;rsquo; means it will be restarted in pretty much any case but us stopping it cleanly. The &lt;code&gt;[multi-user.target](https://unix.stackexchange.com/questions/506347/why-do-most-systemd-examples-contain-wantedby-multi-user-target)&lt;/code&gt; bit is saying we want this service up and running for the system to be considered ready as a server.&lt;/p&gt;
&lt;p&gt;Once that file is in place, we can reload the configs, and start the service, this can be from anywhere, including a user home directory, and check it&amp;rsquo;s status.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl daemon&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;reload
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl start test&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-07-16-at-8.10.32-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;That all looks good, and if I visit the endpoint, there&amp;rsquo;s the expected response, even after we&amp;rsquo;ve logged out of the server.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-07-16-at-8.13.04-pm.png" alt=""&gt;&lt;/p&gt;</description></item></channel></rss>