November 09, 2025 8 min read
After running this Jekyll-based website for a couple years (since July of 2023), I’ve learned valuable lessons about optimizing Jekyll sites for GitHub Pages. This blog initially started as a consolidation of several blog websites I’d published over the years. This is Part 1 of a two-part series covering the foundational optimizations that have made the biggest difference for my homelab blog.
The Journey So Far
This website started as a fork of the excellent Contrast theme by Niklas Buschmann. While the theme provided a solid foundation, running a technical blog with 100+ posts revealed areas for improvement in performance, SEO, and user experience.
Current Website Statistics
Jekyll Version : 4.4.1
Posts : 100+ technical articles
Comments : GitHub Discussions via Giscus
Analytics : Google Analytics (G-F90DVB199P)
Monetization : Google AdSense (ca-pub-2421538118074948)
Pagination : 4 posts per page
One of the most requested features for technical blogs is the ability to copy code snippets easily. I implemented JavaScript-based copy buttons for all code blocks:
// Add copy buttons to all code blocks
document . querySelectorAll ( ' pre code ' ). forEach (( block ) => {
const button = document . createElement ( ' button ' );
button . className = ' copy-button ' ;
button . textContent = ' Copy ' ;
button . addEventListener ( ' click ' , () => {
navigator . clipboard . writeText ( block . textContent );
button . textContent = ' Copied! ' ;
setTimeout (() => button . textContent = ' Copy ' , 2000 );
});
block . parentNode . appendChild ( button );
});
2. Reading Time Indicator
Readers appreciate knowing how long an article will take to read. I added a reading time calculator:
{% assign words = content | number_of_words %}
{% assign reading_time = words | divided_by : 200 %}
{% if reading_time < 1 %}
Less than 1 minute read
{% else %}
{{ reading_time }} minute read
{% endif %}
3. Optimized Image Loading
For technical posts with many screenshots, image optimization is crucial:
[  {:width="50%" height="50%" style="display:block; margin-left:auto; margin-right:auto"}](/assets/images/image.png){:target="_blank"}
This approach:
Provides responsive sizing
Centers images automatically
Opens full-size images in new tabs
Maintains aspect ratios
SEO Enhancements
Proper meta tags significantly improve search engine visibility:
<meta name= "description" content= "After running this Jekyll-based website for a couple years (since July of 2023), I’ve learned valuable lessons about optimizing Jekyll sites for GitHub Pages..." >
<meta property= "og:title" content= "Jekyll Website Optimization for GitHub Pages - Part 1" >
<meta property= "og:description" content= "After running this Jekyll-based website for a couple years (since July of 2023), I’ve learned valuable lessons about optimizing Jekyll sites for GitHub Pages..." >
<meta property= "og:type" content= "article" >
<meta property= "og:url" content= "https://mcgarrah.org/jekyll-website-optimization-part-1/" >
2. Sitemap Generation
GitHub Pages automatically generates sitemaps, but custom optimization helps:
# _config.yml
plugins :
- jekyll-sitemap
- jekyll-feed
- jekyll-seo-tag
url : " https://www.mcgarrah.org"
baseurl : " "
The jekyll-feed plugin generates RSS feeds automatically, but customization improves subscriber experience:
---
layout: null
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>{{ site . title }} </title>
<description>{{ site . description }} </description>
<link>{{ site . url }} </link>
{% for post in site . posts limit : 20 %}
<item>
<title>{{ post . title }} </title>
<description>{{ post . excerpt | strip_html }} </description>
<link>{{ site . url }}{{ post . url }} </link>
<pubDate>{{ post . date | date_to_rfc822 }} </pubDate>
</item>
{% endfor %}
</channel>
</rss>
Content Management Improvements
Organizing technical content requires a robust tagging system:
# Front matter example
---
title : " Article Title"
layout : post
categories : [ technical , homelab ]
tags : [ proxmox , ceph , networking , hardware ]
published : true
---
2. Archive Pages
Created dynamic archive pages for better content discovery:
{% for post in site . posts %}
{% assign year = post . date | date : "%Y" %}
{% assign month = post . date | date : "%B" %}
{% if year != previous_year %}
<h2>{{ year }} </h2>
{% endif %}
{% if month != previous_month %}
<h3>{{ month }} </h3>
{% endif %}
<article>
<a href="{{ post . url }} ">{{ post . title }} </a>
<time>{{ post . date | date : "%B %d, %Y" }} </time>
</article>
{% assign previous_year = year %}
{% assign previous_month = month %}
{% endfor %}
3. Related Posts Section
Implemented related posts based on tags and categories:
{% assign related_posts = site . related_posts | where_exp : "post" , "post.url != page.url" | slice : 0 , 3 %}
{% if related_posts . size > 0 %}
<section class="related-posts">
<h3>Related Articles</h3>
{% for post in related_posts %}
<article>
<a href="{{ post . url }} ">{{ post . title }} </a>
<p>{{ post . excerpt | strip_html | truncate : 100 }} </p>
</article>
{% endfor %}
</section>
{% endif %}
GitHub Actions Workflow
Automated builds ensure consistent deployment:
name : Deploy Jekyll site to Pages
on :
# Runs on pushes targeting the default branch
push :
branches : [ " main" ]
# Runs at 05:00 UTC (01:00 AM EST) every day
schedule :
- cron : ' 5 0 * * *'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch :
jobs :
build-and-deploy :
...
Details for this build can be found in my repository under .github\workflows\jekyll.yml for expanded details.
This above Github Actions automation facilitates future dated articles being published since I also setup my _config.yml to not post future articles.
# _config.yml
# Future posts disabled for scheduled releases via GHA scheduled job
future : false
This combination of settings allows me to bank up several articles to post at future dates once checked into the repository.
Replaced traditional comment systems with GitHub Discussions via Giscus:
<script src= "https://giscus.app/client.js"
data-repo= "mcgarrah/mcgarrah.github.io"
data-repo-id= "R_kgDOKBKIdw"
data-category= "General"
data-category-id= "DIC_kwDOKBKId84Cq3DK"
data-mapping= "pathname"
data-strict= "0"
data-reactions-enabled= "1"
data-emit-metadata= "0"
data-input-position= "bottom"
data-theme= "preferred_color_scheme"
data-lang= "en"
crossorigin= "anonymous"
async >
</script>
Benefits:
No database required
Spam protection via GitHub
Familiar interface for developers
Markdown support in comments
What’s Next
This covers the foundational optimizations that every Jekyll site should implement. In Part 2 of this series , I’ll dive into:
Advanced analytics and monitoring with Google Analytics 4
Custom event tracking for technical content
Remaining optimization priorities (dark/light theme, search, etc.)
Performance measurement results and Core Web Vitals
Lessons learned from running 100+ technical posts
Future enhancement roadmap and infrastructure improvements
Conclusion
These foundational optimizations provide immediate value to readers and establish a solid base for further improvements. The combination of performance enhancements, SEO basics, and content organization creates a much better user experience.
Key takeaways from Part 1:
Code copy buttons are essential for technical content
Proper meta tags significantly improve search visibility
Content organization helps readers find related information
GitHub Actions automate deployment and reduce errors
Giscus comments provide engagement without database overhead
Stay tuned for Part 2, where we’ll explore advanced optimizations and dive deeper into performance metrics and future enhancements.
References