Nerdy Notes

This entire website is hosted for free on my home server!

There are quite a few tools and solutions that I've had to use in order to make this possible:

  1. Unraid - This is the software that my home server runs on. I originally built my server as a way to store and backup my photos at home (with built in redundancy). Originally, I learned about Unraid by watching YouTube videos from Linus Tech Tips. It's fairly easy to setup and maintain, but the Unraid platform can do so much more.
  2. Cloudflare - This is a must! It's a free CDN that protects the security of my computer, doesn't waste my bandwidth, and makes this site IPv6 compatible! It's the secret sauce that serves my website to the world. Without it, the performance of my website would be terrible and I wouldn't be able to self host at all.
  3. Community Apps / Docker - Here's where the power of Unraid can really be utilized. There are tons of free Docker apps that once up and running are very easy to update and maintain. These dockers make hosting this site possible. I'll be discussing them as follows.
  4. CloudFlareDDNS - This docker communicates with Cloudflare, and automatically updates their DNS records to be able to send traffic to your home server. A crucial step, is that you'll need to configure your network's router to route incoming web traffic to your Unraid server. Incidentally, the Comcast Xfinity modem/router wouldn't allow this, so I had to change my network setup to include a third party Netgear Orbi router.
  5. NginxProxyManager - Once the traffic is routed to your machine, it has to be pointed at a specific subnet address. NgingProxyManager does that. With it, you can have countless domains and subdomains all pointed to your home network and serve as many separate sites as you could possibly wish. This software also makes it incredibly easy to get an encryption certificate for a root domain that you can apply to all of your subdomains. Quite remarkable!
  6. MariaDB - A free and easy to use MySQL replacement. A database is necessary for Wordpress and Ghost dockers (see below) to operate. It only takes a few lines of code to configure it and set it up for your blogs, then can be left alone.
  7. Ghost - This is the blogging platform that I'm using for this site. There's things I like about it - lightweight, easy to get around, easy to make edits, and good performance. However, there are so many things that I absolutely hate about it - no image galleries, enables a pay / subscription model by default that you have to disable, no native search, no native comments, and a terrible image gallery that maxes out at nine pictures without an integrated lightbox. Furthermore, once you upload an image, it's stuck on your server. There's no way to delete that image from within the Ghost software, you have to go into the servers folders and manually delete them. There's limited themes for Ghost, and some require a lot of tweaking to get them to function the way you want. Lastly, by default the amount of duplicate and resized images are ridiculous. There's not even and easy way to disable all of the image compression and resizing shenanigans going on. I'm fully capable of uploading my images already optimized the way I like - I don't need or want their software doing it for me using settings that I'm not pleased with. So why do I use it? Because I hate Wordpress even more!
  8. Wordpress - My old site, bershatsky.com is hosted using all of the above solutions as well (except Ghost of ghost). It's even on the same server as this site, but uses a different docker. Wordpress actually does everything I need it to do, and can look great. So why am I not using it? It's just kludgy and full of a million different plugins to get it to do what you want. It's just a big bloated beast. I don't enjoy posting on it at all. It's so unenjoyable, I've actually posted way less than I would like to because it's such an unpleasant experience.
  9. fslightbox.js - This javascript creates a swipe style lightbox on my blog. I had to edit the file to make the background colors 100% black. This is because the page will randomly scroll up and down while swiping, so rather than fix it (because I don't know how), I just made it so you can see it scrolling. You can find my tweaked version of the lightbox, as well as the tweaked theme I'm using for this blog, over on my Google Drive.

Ghost and Casper Theme Tweaks

Casper theme customization in order to prevent image resizing:

index.hbs

Replace this:
        <img class="site-header-cover"
            srcset="{{img_url @site.cover_image size="s"}} 300w,
                    {{img_url @site.cover_image size="m"}} 600w,
                    {{img_url @site.cover_image size="l"}} 1000w,
                    {{img_url @site.cover_image size="xl"}} 2000w"
            sizes="100vw"
            src="{{img_url @site.cover_image size="xl"}}"
            alt=""
        />

with this:

	<img class="site-header-cover"
               src="{{@site.cover_image}}"
                alt="{{title}}"
                />


page.hbs

Replace this:
            <img
                srcset="{{img_url feature_image size="s"}} 300w,
                        {{img_url feature_image size="m"}} 600w,
                        {{img_url feature_image size="l"}} 1000w,
                        {{img_url feature_image size="xl"}} 2000w"
                sizes="(min-width: 1400px) 1400px, 92vw"
                src="{{img_url feature_image size="xl"}}"
                alt="{{title}}"
            />

with this:

           <img class="article-image"
               src="{{img_url feature_image}}"
                alt="{{title}}"
                />


post.hbs

Replace this:

            <img
                srcset="{{img_url feature_image size="s"}} 300w,
                        {{img_url feature_image size="m"}} 600w,
                        {{img_url feature_image size="l"}} 1000w,
                        {{img_url feature_image size="xl"}} 2000w"
                sizes="(min-width: 1400px) 1400px, 92vw"
                src="{{img_url feature_image size="xl"}}"
                alt="{{title}}"
            />

with this:

           <img class="article-image"
               src="{{img_url feature_image}}"
                alt="{{title}}"
                />


tag.hbs

Replace this:

                    <img class="post-card-image"
                        srcset="{{img_url feature_image size="s"}} 300w,
                                {{img_url feature_image size="m"}} 600w,
                                {{img_url feature_image size="l"}} 1000w,
                                {{img_url feature_image size="xl"}} 2000w"
                        sizes="(max-width: 1000px) 400px, 800px"
                        src="{{img_url feature_image size="m"}}"
                        alt="{{title}}"
                        loading="lazy"
                    />

with this:

	<img class="post-card-image"
    src="{{img_url feature_image}}"
    alt="{{title}}"    
    loading="lazy"
        />


In partials directory post-card.hbs

Replace this:

        <img class="post-card-image"
            srcset="{{img_url feature_image size="s"}} 300w,
                    {{img_url feature_image size="m"}} 600w,
                    {{img_url feature_image size="l"}} 1000w,
                    {{img_url feature_image size="xl"}} 2000w"
            sizes="(max-width: 1000px) 400px, 800px"
            src="{{img_url feature_image size="m"}}"
            alt="{{title}}"
            loading="lazy"
        />

With this:

	<img class="post-card-image"
    src="{{img_url feature_image}}"
    alt="{{title}}"    
    loading="lazy"
        />

config.production.json tweaks to prevent image resizing:

"imageOptimization": {
        "resize": false,
        "srcsets": false
    }

<script>
const images = document.querySelectorAll('.kg-image-card img, .kg-gallery-card img');

// Lightbox function
images.forEach(function (image) {
  var wrapper = document.createElement('a');
  wrapper.setAttribute('data-no-swup', '');
  wrapper.setAttribute('data-fslightbox', '');
  wrapper.setAttribute('href', image.src);
  wrapper.setAttribute('aria-label', 'Click for Lightbox');
  image.parentNode.insertBefore(wrapper, image.parentNode.firstChild);
  wrapper.appendChild(image);
});

refreshFsLightbox();
</script>
<script src="/assets/js/fslightbox.js">
</script>

<style>
a.gh-head-button,
.author-list,
.author-name,
.footer-cta,
.post-card-byline-content span:first-of-type {
    display: none;
    }
.post-card-byline-content {
    margin-left: 0;
    }
.site-footer {
    padding-bottom: 40px;
    }
.read-more-wrap {
    padding-bottom: 4vmin;
    }
.article-header {
    padding-bottom: 4vmin;
}
</style>

For multiple tags on the homepage, edit post-card.hbs:

        <a class="post-card-content-link" href="{{url}}">
            <header class="post-card-header">
	    <div class="post-card-primary-tag">
                {{#if tags}}
                   {{#foreach tags}}
                   {{name}}&nbsp;&nbsp;&nbsp;
		  {{/foreach}}
		{{/if}}
		</div>
                <h2 class="post-card-title">{{title}}</h2>	
            </header>
            <section class="post-card-excerpt">
                <p>{{excerpt}}</p>
            </section>
        </a>

For multiple tags on posts, edit post.hbs:

        {{#if tags}}
	<section class="article-tag">
	{{#foreach tags}}
	<a href="{{url}}" title="{{name}}" class="tag-{{id}}">
	{{name}}&nbsp;&nbsp;&nbsp;
	</a>
	{{/foreach}}
	</section>
	{{/if}}

Add DuckDuckGo Search to site:

<form method="get" id="search" action="https://duckduckgo.com/">
<input type="hidden" name="sites" value="bershatsky.net"/>
<input class="search" type="text" name="q" placeholder="Search Bershatsky.net"/>
<input type="submit" value="Go!"></form>