<?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: Minh Nguyen (he/him)</title>
    <description>The latest articles on DEV Community by Minh Nguyen (he/him) (@contradicthelaw).</description>
    <link>https://dev.to/contradicthelaw</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%2F30317%2F2c9b04a7-a8e2-49cc-a0f8-c7fe048e1285.jpg</url>
      <title>DEV Community: Minh Nguyen (he/him)</title>
      <link>https://dev.to/contradicthelaw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/contradicthelaw"/>
    <language>en</language>
    <item>
      <title>How to use MailHog in Lando</title>
      <dc:creator>Minh Nguyen (he/him)</dc:creator>
      <pubDate>Mon, 08 Feb 2021 05:00:00 +0000</pubDate>
      <link>https://dev.to/contradicthelaw/how-to-use-mailhog-in-lando-3377</link>
      <guid>https://dev.to/contradicthelaw/how-to-use-mailhog-in-lando-3377</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@charlottelharrison?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Charlotte Harrison&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/postbox?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Unsplash&lt;/a&gt;.
&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you’ve ever needed a service to capture e-mails, &lt;a href="https://github.com/mailhog/MailHog"&gt;MailHog&lt;/a&gt; is a pretty good choice. MailHog contains an SMTP server which you send messages to, simulating your production SMTP service, and provides a HTTP interface so you can see what’s being sent.&lt;/p&gt;

&lt;p&gt;It isn’t a substitute for your production mail service or e-mail client, so you’ll still need to plug it all in for testing, and make sure to check in a variety of e-mail clients. This is good if you need to debug why e-mail clients are rendering your messages strangely and need to experiment.&lt;/p&gt;

&lt;p&gt;With that in mind, I’ll show you how to add a MailHog service in &lt;a href="https://lando.dev"&gt;Lando&lt;/a&gt; via the Landofile &lt;code&gt;.lando.yml&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You know how to run Lando&lt;/li&gt;
&lt;li&gt;You have an application which sends messages via SMTP&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting up your Landofile
&lt;/h2&gt;

&lt;p&gt;In your Landofile (&lt;code&gt;.lando.yml&lt;/code&gt;), add a new section for services. If your Landofile already has one, you can append to it instead. Below I define a new service called &lt;code&gt;mail&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;# .lando.yml
# ...the rest of the YAML config sits above here

services:
  mail:
    type: mailhog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re almost ready to go, but we need to provide information about which services need to be able to access MailHog. To do that, define a key labelled &lt;code&gt;hogfrom&lt;/code&gt;, and list all our services. In this case, &lt;code&gt;appserver&lt;/code&gt; is responsible for sending our message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .lando.yml
# ...the rest of the YAML config sits above here  

services:
  mail:
    type: mailhog
    hogfrom:
      - appserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;lando rebuild&lt;/code&gt;, which should rebuild your stack, including the new MailHog service. In the output where you see a list of URLs you can access your app from, you should now see a &lt;code&gt;localhost&lt;/code&gt; URL that you can open to access the MailHog interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring your web app
&lt;/h2&gt;

&lt;p&gt;In your web app, you will need to change the SMTP configuration so it points at MailHog. In Drupal, this can be done by overriding the system configuration in &lt;code&gt;settings.local.php&lt;/code&gt;. This may differ depending on your specific application, so I can’t help with that.&lt;/p&gt;

&lt;p&gt;In terms of credentials and URLs, here is what you can use to connect to MailHog’s SMTP server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hostname: &lt;code&gt;mail&lt;/code&gt;—same as the name we gave the service above&lt;/li&gt;
&lt;li&gt;Port: 1025&lt;/li&gt;
&lt;li&gt;Username: as desired&lt;/li&gt;
&lt;li&gt;Password: as desired&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Couldn’t I just run MailHog outside of Lando or Docker?
&lt;/h3&gt;

&lt;p&gt;Sure, but Lando exists as a way to lock down those tools so your colleagues don’t have to figure out how to install MailHog, or ask you how to install it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wasn’t MailHog development suspended?
&lt;/h3&gt;

&lt;p&gt;It was for some time, but development picked up again and a new release &lt;a href="https://github.com/mailhog/MailHog/releases/tag/v1.0.1"&gt;1.0.1 was put out August 12 2020&lt;/a&gt;. I was a bit surprised as well, since the last official release 1.0.0 was in 2017.&lt;/p&gt;

</description>
      <category>guide</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>lando</category>
    </item>
    <item>
      <title>How to install ClamAV in Lando for Drupal</title>
      <dc:creator>Minh Nguyen (he/him)</dc:creator>
      <pubDate>Tue, 02 Feb 2021 23:00:00 +0000</pubDate>
      <link>https://dev.to/contradicthelaw/how-to-install-clamav-in-lando-for-drupal-977</link>
      <guid>https://dev.to/contradicthelaw/how-to-install-clamav-in-lando-for-drupal-977</guid>
      <description>&lt;p&gt;&lt;em&gt;
  Photo by &lt;a href="https://unsplash.com/@nbandana?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;N Bandaru&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/shell?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Unsplash&lt;/a&gt;.
&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;ClamAV is an antivirus software service which allows servers to scan for viruses. While it is notable for use in scanning for viruses on mail servers, we can also use it in our web applications to scan file uploads.&lt;/p&gt;

&lt;p&gt;I’m doing some local development on a Drupal based site, which makes use of &lt;a href="https://www.drupal.org/project/clamav"&gt;ClamAV through a contributed module&lt;/a&gt; to scan files uploaded to the site.&lt;/p&gt;

&lt;p&gt;Having the module enabled but not having ClamAV on the system throws an error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bOoNOtpi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/static/bfa3a48eea426e8650bce6d1895a8f91/a1dd2/drupal-clamav-upload-error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bOoNOtpi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/static/bfa3a48eea426e8650bce6d1895a8f91/a1dd2/drupal-clamav-upload-error.png" alt="Error message from Drupal whilst uploading a file, reading 'The anti-virus scanner could not check the file, so the file cannot be uploaded.'" title="Error message from Drupal whilst uploading a file, reading 'The anti-virus scanner could not check the file, so the file cannot be uploaded.'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could just uninstall the ClamAV module and work without it, but that introduces a potential gap in your test coverage—the behaviour of the site operates differently without the module installed, which could lead to issues that you don’t discover until later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You know how to run &lt;a href="https://lando.dev/"&gt;Lando&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Your application is configured to scan file uploads e.g. in Drupal, the &lt;a href="https://www.drupal.org/project/clamav"&gt;ClamAV module&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting up your Landofile
&lt;/h2&gt;

&lt;p&gt;In your Landofile (&lt;code&gt;.lando.yml&lt;/code&gt;), we will use the services section to modify the main application service—in our case labelled &lt;code&gt;appserver&lt;/code&gt; to install ClamAV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .lando.yml
# ...the rest of the YAML config sits above here

services:
  appserver:
    build_as_root:
      - apt-get update -y
      - apt-get install clamav clamav-daemon -y
      - echo "TCPSocket 3310" &amp;gt;&amp;gt; /etc/clamav/clamd.conf
      - freshclam
      - update-rc.d clamav-daemon enable
    run_as_root:
      - service clamav-daemon start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up the commands to run that installs ClamAV, sets up the ability to connect to the service on port &lt;code&gt;3310&lt;/code&gt;, update the database definitions necessary to scan files, and then enable the ClamAV service.&lt;/p&gt;

&lt;p&gt;Note here that we are using &lt;code&gt;apt-get&lt;/code&gt; to install the packages as the base container images that Lando uses are from Debian. You may need to adjust if your base distribution is different.&lt;/p&gt;

&lt;p&gt;Once the changes are made, rebuild the service with &lt;code&gt;lando rebuild&lt;/code&gt;. This will relaunch your application, install ClamAV into your local container, and start it.&lt;/p&gt;

&lt;p&gt;I can now load up my environment and confirm that ClamAV is successfully loaded, and that file uploads work:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9hMvVF9u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/static/70dfc84c2f993385858954dcf8c43386/44fd6/drupal-clamav-upload-success.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9hMvVF9u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/static/70dfc84c2f993385858954dcf8c43386/44fd6/drupal-clamav-upload-success.png" alt="File upload success" title="File upload success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, we can use the &lt;a href="https://en.wikipedia.org/wiki/EICAR_test_file"&gt;EICAR test file&lt;/a&gt; to confirm that it does block viruses from being uploaded:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bs5Yww6W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/static/59f3aee853e6c0422040c8f353e5b8d1/0ad97/drupal-clamav-upload-blocked.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bs5Yww6W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/static/59f3aee853e6c0422040c8f353e5b8d1/0ad97/drupal-clamav-upload-blocked.png" alt="A file we try to upload, but was blocked by the anti-virus" title="A file we try to upload, but was blocked by the anti-virus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible future changes
&lt;/h2&gt;

&lt;p&gt;I could set out these changes in a custom service, or alternatively use a prebuilt container for ClamAV. Of course, the current hosting environment assumes ClamAV running on &lt;code&gt;localhost&lt;/code&gt; on a TCP port so it’s not practical to modify it for that yet.&lt;/p&gt;

&lt;p&gt;Hopefully this has been a useful guide in how to have ClamAV set up for your Drupal site. If you’re not running Drupal, hopefully you’ll find it useful if you’re integrating ClamAV for your own applications and infrastructure.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>guide</category>
      <category>tutorial</category>
      <category>drupal</category>
    </item>
    <item>
      <title>Annotating my pages with microformats2 and h-entry</title>
      <dc:creator>Minh Nguyen (he/him)</dc:creator>
      <pubDate>Tue, 06 Oct 2020 12:44:33 +0000</pubDate>
      <link>https://dev.to/contradicthelaw/annotating-my-pages-with-microformats2-and-h-entry-22gd</link>
      <guid>https://dev.to/contradicthelaw/annotating-my-pages-with-microformats2-and-h-entry-22gd</guid>
      <description>&lt;p&gt;h-entry is an open microformat for datestamped content on the web. I was looking at ways to make my site a bit more open to syndication, and also as an alternative to RSS/Atom feeds. I haven’t seen major browsers pick up on these formats, but the main focus are IndieWeb readers, and I’m all for them.&lt;/p&gt;

&lt;p&gt;Now, please note that this post isn’t much of a guide to implementing h-entry or other parts of the microformats2 spec on your site, as it is a summary of I went about implementing it for this website.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;items&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="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h-entry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;properties&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My blog post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&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="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Smith&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2020-01-01 12:00:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A short summary of my blog post, and what you can do.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content&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="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Starting paragraph of my blog post.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;p&amp;gt;Starting paragraph of my blog post.&amp;lt;/p&amp;gt;&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In short, there’s basically all of the information about the post, in a JSON object that could be easily be used as part of a different app. With that in mind, how does a parser go about doing this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Elements of h-entry
&lt;/h2&gt;

&lt;p&gt;h-entry itself is a what is known as a root class name. We can use it to define a h-entry, and it has a series of properties that the parser will determine, based on knowing that. Other root class names include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;h-adr&lt;/li&gt;
&lt;li&gt;h-event&lt;/li&gt;
&lt;li&gt;h-geo&lt;/li&gt;
&lt;li&gt;h-item&lt;/li&gt;
&lt;li&gt;h-product&lt;/li&gt;
&lt;li&gt;h-recipe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find a more detailed list of these on the &lt;a href="https://microformats.org/wiki/microformats2"&gt;microformats2 page&lt;/a&gt;, along with examples and detailed descriptions.&lt;/p&gt;

&lt;p&gt;Back to h-entry, we can establish this simply by naming it as a class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-entry"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and that’s it, our article is now parseable as it is, though it might not be very useful by itself. This is where we add some core h-entry properties!&lt;/p&gt;

&lt;h2&gt;
  
  
  h-entry properties
&lt;/h2&gt;

&lt;p&gt;There’s quite an amount of these, so I’ll only go with the properties that I used for this site.&lt;/p&gt;

&lt;h3&gt;
  
  
  p-name
&lt;/h3&gt;

&lt;p&gt;This represents the entry name or title. For the title, we just need to add this class to the element that contains it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"p-name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{post.frontmatter.title}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  e-content
&lt;/h3&gt;

&lt;p&gt;The full content of the entry in question i.e. the full body of the blog post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"e-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dt-published
&lt;/h3&gt;

&lt;p&gt;The datestamp of when the entry was published:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dt-published"&lt;/span&gt; &lt;span class="na"&gt;datetime=&lt;/span&gt;&lt;span class="s"&gt;"2020-09-29"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;29 September, 2020&lt;span class="nt"&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing h-entry within a Gatsby content type
&lt;/h2&gt;

&lt;p&gt;Let’s get topical! For this, I’m using &lt;a href="https://mnguyen.io/blog/my-gatsby-setup/"&gt;Gatsby&lt;/a&gt; as my site generator, so in order to get this working for my blog post, I need to modify the template for the blog post’s render function (located in &lt;code&gt;/src/templates/blog-post.js&lt;/code&gt;), which returns JSX as follows:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markdownRemark&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Layout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SEO&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h-entry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`0.5rem`&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;draft&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`Draft`&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dt-published&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;dateTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;htmldate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/time&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Layout&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the 2 &lt;code&gt;section&lt;/code&gt;, the &lt;code&gt;h1&lt;/code&gt;, and the &lt;code&gt;time&lt;/code&gt; elements are updated as above to have the microformat2 elements defined via the &lt;code&gt;className&lt;/code&gt; attribute. While I was able to get it working on these generic HTML elements, I wasn’t able to get it working for the &lt;code&gt;time&lt;/code&gt; element, which was previously a separate element called &lt;code&gt;&amp;lt;DateTime&amp;gt;&lt;/code&gt;. I couldn’t figure it out, so I reverted to using the base element that it represented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://microformats.org/wiki/microformats2"&gt;microformats2 on microformats.org&lt;/a&gt;: has a lot of information about microformat types that I’ve only just touched on in this post.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://microformats.org/wiki/h-entry"&gt;h-entry on microformats.org&lt;/a&gt;: provides information specific about h-entry, including some other optional properties I didn’t use in this post.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://indieweb.org/h-entry"&gt;h-entry on indieweb.org&lt;/a&gt;: has some extra reasoning why you should use h-entry, as well as examples and use cases spanning from it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementing a microformat isn’t difficult, and can provide new ways for people to access your content. Give it a go!&lt;/p&gt;

</description>
      <category>microformats</category>
      <category>parsing</category>
      <category>gatsby</category>
      <category>html</category>
    </item>
    <item>
      <title>Running NixOS in Production</title>
      <dc:creator>Minh Nguyen (he/him)</dc:creator>
      <pubDate>Thu, 17 Sep 2020 08:16:00 +0000</pubDate>
      <link>https://dev.to/contradicthelaw/running-nixos-in-production-2nj5</link>
      <guid>https://dev.to/contradicthelaw/running-nixos-in-production-2nj5</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6razYbCV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/files/91e49ba4259b9d2e13357e23d2290bdc/nixos-social-preview.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6razYbCV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mnguyen.io/files/91e49ba4259b9d2e13357e23d2290bdc/nixos-social-preview.svg" alt="NixOS logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/NixOS/nixos-artwork/tree/master/logo" rel="noopener"&gt;NixOS logo&lt;/a&gt; created by Tim Cuthbertson (&lt;a href="https://twitter.com/timbertson" rel="noopener"&gt;@timbertson&lt;/a&gt;), licenced under &lt;a href="https://creativecommons.org/licenses/by/4.0/" rel="noopener"&gt;CC-BY 4.0&lt;/a&gt;.
&lt;/p&gt;



&lt;p&gt;I’ve moved some items from my previous site infrastructure over to a VPS powered running NixOS in the past weekend. What made me do it? There were a few reasons that came to mind for it.&lt;/p&gt;

&lt;p&gt;The first is that I’m sort of done running on OpenVZ—the performance is pretty bare compared to other options out there. There’s only a few choices for operating systems to run on it. Ubuntu 16.04 (Xenial) is okay to work with, but on the limited resources, things always took a lot longer than they should have, whether it was running &lt;a href="https://ghost.org/"&gt;Ghost&lt;/a&gt; or trying to build this site on Gatsby.&lt;/p&gt;

&lt;p&gt;Another reason for doing this is that I don’t remember a lot of the things I did setting up the site. Could I tell you I installed &lt;a href="https://certbot.eff.org/"&gt;Certbot&lt;/a&gt;? Nope. Could I recite the hundred word set of compiler arguments to make I compiled &lt;a href="https://www.modpagespeed.com/doc/build_ngx_pagespeed_from_source"&gt;nginx with Pagespeed&lt;/a&gt; support? Nope. Would I know how to update PHP from 7.2 once it reaches &lt;a href="https://www.php.net/supported-versions.php"&gt;end of life&lt;/a&gt;? Nope—I’d have to look that one up (and I already have to do that in my day to day work!).&lt;/p&gt;

&lt;p&gt;I could have picked any other OS to operate, like Debian or CentOS. But I ended up on NixOS for the following reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I wanted to learn more about NixOS, and how to run through application deployment and management to see how it stacks up against other methods.&lt;/li&gt;
&lt;li&gt;The idea of declaring my system configuration as code I could deploy was interesting to me, since I spend a lot of time running through package installation and configuration in my day-to-day—making that process easier would definitely give me time to do other things.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Some of the details
&lt;/h2&gt;

&lt;p&gt;For some context, I have Nix installed on my Arch Linux system (there’s a useful guide to install it &lt;a href="https://wiki.archlinux.org/index.php/Nix"&gt;on their wiki&lt;/a&gt;). From there, it was a matter of putting together a few pieces of configuration. The build script itself is powered by Elixir—mainly a result of the fact I couldn’t get the Haskell script in the guide I was following to work.&lt;/p&gt;

&lt;p&gt;The VPS is an Amazon EC2 instance, which was created using the &lt;a href="https://nixos.org/download.html"&gt;instructions provided by NixOS&lt;/a&gt; to launch a provided Amazon Machine Image (AMI).&lt;/p&gt;

&lt;p&gt;The first stop is &lt;code&gt;server.nix&lt;/code&gt;, which is built like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;
  &lt;span class="nv"&gt;nixos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;nixpkgs/nixos&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="sx"&gt;./configuration.nix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kn"&gt;in&lt;/span&gt;
  &lt;span class="nv"&gt;nixos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;configuration.nix&lt;/code&gt; contains the bulk of the configuration which is set up on the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pkgs&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="kd"&gt;let&lt;/span&gt;
  &lt;span class="nv"&gt;unstable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;nixos-unstable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kn"&gt;in&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;nixpkgs/nixos/modules/virtualisation/amazon-image.nix&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="sx"&gt;./users.nix&lt;/span&gt; &lt;span class="sx"&gt;./firewall.nix&lt;/span&gt; &lt;span class="sx"&gt;./webserver.nix&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nv"&gt;ec2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;hvm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;networking&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;hostName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mnguyen-nix-demo"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;systemPackages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nv"&gt;unstable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy2&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fish&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;htop&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mosh&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;vim&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nv"&gt;programs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fish&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;# sudo without requiring password&lt;/span&gt;
  &lt;span class="nv"&gt;security&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;wheelNeedsPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;## Enable BBR module&lt;/span&gt;
  &lt;span class="nv"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;kernelModules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"tcp_bbr"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c"&gt;## Network hardening and performance&lt;/span&gt;
  &lt;span class="nv"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;sysctl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# Disable magic SysRq key&lt;/span&gt;
    &lt;span class="s2"&gt;"kernel.sysrq"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Ignore ICMP broadcasts to avoid participating in Smurf attacks&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.icmp_echo_ignore_broadcasts"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Ignore bad ICMP errors&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.icmp_ignore_bogus_error_responses"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Reverse-path filter for spoof protection&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.default.rp_filter"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.all.rp_filter"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# SYN flood protection&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.tcp_syncookies"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Do not accept ICMP redirects (prevent MITM attacks)&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.all.accept_redirects"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.default.accept_redirects"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.all.secure_redirects"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.default.secure_redirects"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv6.conf.all.accept_redirects"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv6.conf.default.accept_redirects"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Do not send ICMP redirects (we are not a router)&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.all.send_redirects"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Do not accept IP source route packets (we are not a router)&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.conf.all.accept_source_route"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv6.conf.all.accept_source_route"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Protect against tcp time-wait assassination hazards&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.tcp_rfc1337"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# TCP Fast Open (TFO)&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.tcp_fastopen"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;## Bufferbloat mitigations&lt;/span&gt;
    &lt;span class="c"&gt;# Requires &amp;gt;= 4.9 &amp;amp; kernel module&lt;/span&gt;
    &lt;span class="s2"&gt;"net.ipv4.tcp_congestion_control"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bbr"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;# Requires &amp;gt;= 4.19&lt;/span&gt;
    &lt;span class="s2"&gt;"net.core.default_qdisc"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cake"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c"&gt;# disable passwordless SSH&lt;/span&gt;
  &lt;span class="nv"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;openssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;passwordAuthentication&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;# Let trusted users upload unsigned packages&lt;/span&gt;
  &lt;span class="nv"&gt;nix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;trustedUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"@wheel"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c"&gt;# Clean up packages after a while&lt;/span&gt;
  &lt;span class="nv"&gt;nix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;gc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;automatic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;dates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"weekly UTC"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c"&gt;# Disable reinitialisation of AMI on restart or power cycle&lt;/span&gt;
  &lt;span class="nv"&gt;systemd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;amazon-init&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;swapDevices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/swapfile"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nv"&gt;systemd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fathom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Fathom Server"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;requires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"network.target"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"network.target"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;wantedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"multi-user.target"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;serviceConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"simple"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"minh"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;Restart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"on-failure"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;RestartSec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;WorkingDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/var/lib/fathom"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;ExecStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/minh/bin/fathom --config=/etc/fathom.env server"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;imports&lt;/code&gt; pulls in specific NixOS configuration for EC2 which it’s running on, and I’ve started work separating out configuration to separate files, &lt;code&gt;./users.nix&lt;/code&gt;, &lt;code&gt;./firewall.nix&lt;/code&gt;, and &lt;code&gt;./webserver.nix&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packages
&lt;/h3&gt;

&lt;p&gt;You can see near the top of &lt;code&gt;configuration.nix&lt;/code&gt; there’s a variable called &lt;code&gt;environment.systemPackages&lt;/code&gt;. This lists all the additional software packages I want installed. In this case, I have &lt;a href="https://fishshell.com/"&gt;fish&lt;/a&gt;, &lt;a href="https://htop.dev/"&gt;htop&lt;/a&gt;, &lt;a href="https://mosh.org/"&gt;mosh&lt;/a&gt;, and &lt;a href="https://www.vim.org/"&gt;vim&lt;/a&gt; installed from the standard repo, as well as &lt;a href="https://caddyserver.com/"&gt;Caddy 2&lt;/a&gt; from the unstable NixOS branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Swap
&lt;/h3&gt;

&lt;p&gt;I don’t have any swap on the system, so the variable &lt;code&gt;swapdevices&lt;/code&gt; is set to create a swap file at &lt;code&gt;/swapfile&lt;/code&gt;. The size is defined in megabytes, so this file is 1 gigabyte in size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firewall
&lt;/h3&gt;

&lt;p&gt;NixOS comes with a firewall setup out of the box to only be open for SSH on port 22. To change it up, we can set &lt;code&gt;networking.firewall.allowedTCPPorts&lt;/code&gt; as a list of ports we want to be open.&lt;/p&gt;

&lt;p&gt;I also want to move the default SSH port to something else, this is defined by &lt;code&gt;services.openssh.listenAddresses&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the end, my &lt;code&gt;firewall.nix&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pkgs&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# SSHD Port reassignment&lt;/span&gt;
  &lt;span class="nv"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;openssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;listenAddresses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;37586&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c"&gt;# Allowed TCP range&lt;/span&gt;
  &lt;span class="nv"&gt;networking&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;firewall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;allowedTCPPorts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="mi"&gt;37586&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c"&gt;# Allow Mosh connections&lt;/span&gt;
  &lt;span class="nv"&gt;networking&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;firewall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;allowedUDPPortRanges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60010&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Caddy server
&lt;/h3&gt;

&lt;p&gt;Moving on, I set up the web server within &lt;code&gt;webserver.nix&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pkgs&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="kd"&gt;let&lt;/span&gt;
  &lt;span class="nv"&gt;unstable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;nixos-unstable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="nv"&gt;caddyDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/var/lib/caddy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;caddyConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;writeText&lt;/span&gt; &lt;span class="s2"&gt;"Caddyfile"&lt;/span&gt;
    &lt;span class="s2"&gt;''{&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  storage file_system {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    root /var/lib/caddy&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;mnguyen.io {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  root * /srv/www/mnguyen.io&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  file_server&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  header / {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    X-Content-Type-Options "nosniff"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    X-Frame-Options "sameorigin"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    Referrer-Policy "no-referrer-when-downgrade"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    X-UA-Compatible "IE=edge,chrome=1"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    X-XSS-Protection "1; mode=block"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    Content-Security-Policy "default-src 'self'; connect-src https://analytics.mnguyen.io 'self'; font-src 'self' data:; img-src https://analytics.mnguyen.io 'self' data:; script-src https://analytics.mnguyen.io 'self' 'unsafe-inline'; style-src 'unsafe-inline'; worker-src 'self'; prefetch-src 'self'; report-uri https://mnguyen.report-uri.com/r/d/csp/enforce; report-to https://mnguyen.report-uri.com/r/d/csp/enforce"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  }&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;www.mnguyen.io {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  redir https://mnguyen.io{uri}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;analytics.mnguyen.io {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  reverse_proxy localhost:9000&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;in&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;systemd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Caddy web server"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"network-online.target"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;wants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"network-online.target"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;wantedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"multi-user.target"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;serviceConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"caddy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;Group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"caddy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;ExecStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;unstable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin/caddy run --config &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddyConfig&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --adapter caddyfile&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;ExecReload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;unstable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin/caddy reload --config &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddyConfig&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --adapter caddyfile&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;TimeoutStopSec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5s"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;LimitNOFILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1048576&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;LimitNPROC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;PrivateTmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;ProtectSystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"full"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;AmbientCapabilities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cap_net_bind_service"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"caddy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;uids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;caddyDir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;createHome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;extraGroups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;gid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;uids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;caddy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is set up the &lt;code&gt;Caddyfile&lt;/code&gt; that Caddy 2 will be using, as well as the systemd service which sets up the command, sets the capabilities of the service so it can run at lower port numbers (below 1024 to be precise), and sets up a dedicated &lt;code&gt;caddy&lt;/code&gt; user which it will run under.&lt;/p&gt;

&lt;h3&gt;
  
  
  Users
&lt;/h3&gt;

&lt;p&gt;Speaking of which, users in NixOS can be defined in a few lines. This is what can go into &lt;code&gt;users.nix&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pkgs&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;minh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;isNormalUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;extraGroups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"wheel"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;shell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fish&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c"&gt;# password = "my secure password";&lt;/span&gt;
    &lt;span class="c"&gt;# hashedPassword = "$6$qTK.7QsrnONOr$ZsAfPlnEPLtpiO9j1qp/POkDga2LtK1UOD0nrG497CegYEq5e.E6iHf5tDqwfLViBSWEsw8sn5t885p6HyRgS1";&lt;/span&gt;

    &lt;span class="nv"&gt;openssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;authorizedKeys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"ssh-rsa PUBLICKEYHERE"&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flag &lt;code&gt;isNormalUser&lt;/code&gt; tells NixOS to give us a home directory at &lt;code&gt;/home/minh&lt;/code&gt;. I then set up &lt;code&gt;sudo&lt;/code&gt; access by adding this user to the &lt;code&gt;wheel&lt;/code&gt; group, set the shell to fish, and set the allowed SSH key that is allowed to access this user on the system—I cannot sign in with a password unless it’s explicitly set.&lt;/p&gt;

&lt;p&gt;If I did want to set a password, I could use the insecure option &lt;code&gt;password&lt;/code&gt; which is a string, or slightly more secure &lt;code&gt;passwordHash&lt;/code&gt; (which uses a hash generated from the command &lt;code&gt;mkpasswd -m sha-512&lt;/code&gt;, detailed on the &lt;a href="https://nixos.org/manual/nixos/stable/options.html#opt-users.users._name__.hashedPassword"&gt;passwordHash options page&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Fathom analytics
&lt;/h3&gt;

&lt;p&gt;I pulled the last public available release of &lt;a href="https://github.com/usefathom/fathom/"&gt;Fathom Lite from their repository&lt;/a&gt;, and created a systemd service file to run it. I was worried for a bit that it wouldn’t run, as there were a lot of discussions going on the community forums about having &lt;a href="https://discourse.nixos.org/t/how-to-make-nixos-so-easy-that-people-can-be-productive-up-front-without-having-to-first-learn-the-nix-language/5625"&gt;to patch or otherwise emulate a standard Linux system to make binaries work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thankfully, Fathom isn’t one of those programs, and it worked flawlessly without any other work. It’s not packaged for NixOS, so this is probably the least NixOS part of this system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building NixOS
&lt;/h2&gt;

&lt;p&gt;There are 2 commands to build our NixOS packages and send it to our remote system, then there’s 2 other commands to switch profiles and activate our new setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;nix-build&lt;/code&gt;: which builds the NixOS packages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nix-copy-closure&lt;/code&gt;: sends the NixOS packages to our remote&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nix-env&lt;/code&gt;: Set the Nix profile to our uploaded packages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;switch-to-configuration&lt;/code&gt;: Switch the system configuration to our new configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These all come together in an Elixir script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;server_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"server_address.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;server_processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Build&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;upload_to_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
    &lt;span class="n"&gt;fixed_path_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"nix-copy-closure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"--to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--use-substitutes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fixed_path_str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="no"&gt;Build&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;activate_nix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;activate_nix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/nix/var/nix/profiles/system"&lt;/span&gt;
    &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ssh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sudo nix-env --profile &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --set &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ssh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sudo &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin/switch-to-configuration switch"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"nix-build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"server.nix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--no-out-link"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Build&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upload_to_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_processed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the script runs all of those commands sequentially, and executes all the above. One thing I note about this is that the script hangs if there’s an issue with any of the configuration files, which could lead to a server that’s unresponsive. You may need to power cycle your remote instance if that happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding remarks
&lt;/h2&gt;

&lt;p&gt;I learned quite a bit about NixOS, but like most things involved quite a bit of trial and error. Thankfully, having this sort of configuration means I can easily rebuild the system (such as when I found myself unable to connect via SSH when I changed the port but didn’t realise the firewall needed to be changed as well).&lt;/p&gt;

&lt;p&gt;If you’re interested in NixOS, I’d encourage you to take a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;p&gt;In case you wanted to delve further into some of the resources I looked up while I was getting this set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://typeclasses.com/nixos-on-aws"&gt;Deploying NixOS to Amazon EC2&lt;/a&gt; by Type Classes: This was very useful starting out to understand how to build NixOS locally, and how to ship it off to a different installation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.lafuente.me/posts/local-dns-and-web-server/"&gt;Running a local dns and web server&lt;/a&gt; by José Luis Lafuente: I found the section to define the custom systemd service very useful (just a heads up you want to set &lt;code&gt;home&lt;/code&gt; for the user in that config to the Caddy data directory—mine was &lt;code&gt;/var/lib/caddy&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>nix</category>
    </item>
  </channel>
</rss>
