Understanding Jekyll, Nix, and Local Web Development: Getting Started
Websites are often built using tools that help manage content and structure. One such tool is Jekyll, a popular “static site generator.” This means it takes your raw content (like text written in a simple format called Markdown) and templates (which define the look and feel), and builds them into a complete set of HTML, CSS, and JavaScript files that can be easily hosted online. Many developers use services like GitHub Pages to host these static sites for free. GitHub Pages works seamlessly with Jekyll, automatically rebuilding and publishing your site when you update your content.
However, before publishing online, developers need to work on their sites locally on their own computers. This can become complex if you’re managing multiple Jekyll websites, each potentially needing different settings or software versions. The article you’re about to read delves into creating a sophisticated local development setup using a tool called Nix. Nix helps create reproducible and isolated environments, meaning the setup will work consistently across different computers and prevent conflicts between projects. The author is building this local environment not just for current development needs, but also as a stepping stone towards an even more ambitious goal: “home hosting,” which means running their websites on a server they manage themselves at home, rather than relying on commercial services.
The Journey Begins
In the world of web hosting, there’s a fascinating dance between simplicity and complexity. Commercial web hosts have mastered the art of serving multiple websites from a single IP address using technologies like Server Name Indication (SNI) and name-based virtual hosting. This capability, once the domain of enterprise infrastructure, is now within reach of home users thanks to modern tools and practices.
My journey towards home-hosting multiple websites began with a simple need: to efficiently develop and preview multiple Jekyll-based sites locally before pushing them to GitHub Pages. This seemingly straightforward requirement led to the creation of a robust development environment that not only solves immediate needs but also serves as a stepping stone towards a full home-hosting setup.
Current Setup: GitHub Pages and Jekyll
Like many developers, I host my websites on GitHub Pages. It’s a fantastic service that offers:
- Free hosting for static websites
- Direct integration with Git repositories
- Automatic builds and deployments
- Built-in support for Jekyll
Jekyll, the static site generator powering GitHub Pages, is a powerful tool that:
- Converts Markdown and other text files into static websites
- Provides built-in support for blogs, documentation, and portfolios
- Offers a rich ecosystem of plugins and themes
- Embraces convention over configuration
However, while GitHub Pages handles the hosting, local development requires a reliable environment that can:
- Run multiple Jekyll sites simultaneously
- Handle dependency management effectively
- Provide consistent behavior across different machines
- Automatically detect and fix common issues
The Development Environment Challenge
The challenge of running multiple Jekyll sites locally led to the creation of a Nix-based development environment. Nix, with its declarative approach to system configuration, provides the perfect foundation for a reproducible development setup.
Here’s what we needed to solve:
- Multiple Sites, Multiple Ports: Each Jekyll site needs its own port for local development
- Dependency Management: Ruby gems and their native extensions can be tricky
- Version Conflicts: Ruby version mismatches can break the entire setup
- Environment Isolation: Each site should have its own gem environment
The Solution: A Nix Flake for Jekyll Development
The solution came in the form of a Nix flake that provides a complete development environment. Let’s look at the key components:
1. Environment Configuration
{
description = "Nix flake for Jekyll environment with Rouge for syntax highlighting and Bundler support";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
requests
simplenote
]);
in
{
devShells.default = pkgs.mkShell {
buildInputs = [
pkgs.ruby
pkgs.jekyll
pkgs.rubyPackages_3_2.rouge
pkgs.neovim
pkgs.git
pythonEnv
pkgs.stdenv.cc.cc.lib
pkgs.pkg-config
pkgs.openssl
pkgs.zlib
pkgs.libffi
pkgs.libxml2
pkgs.libxslt
];
};
});
}
2. The jes
Command
The heart of our solution is the jes
(Jekyll Environment Script) command. It handles:
- Automatic port configuration
- Ruby version compatibility checks
- Gem installation and management
- Site-specific server management
jes() {
echo "Jekyll Environment Script v1.0.6"
# Find the Jekyll root (where Gemfile exists)
site_root="$current_dir"
while [[ "$site_root" != "/" && ! -f "$site_root/Gemfile" ]]; do
site_root="$(dirname "$site_root")"
done
# Read port from .port file or use default
if [ -f "$site_root/.port" ]; then
port=$(cat "$site_root/.port")
echo "Using port $port from .port file"
else
port=4000
echo "No .port file found, using default port $port"
fi
}
3. Ruby Version Management
One of the most critical aspects is handling Ruby version mismatches:
check_ruby_version() {
echo "Checking Ruby environment..."
# Save current environment
local old_gem_home="$GEM_HOME"
local old_gem_path="$GEM_PATH"
local old_path="$PATH"
local old_bundle_gemfile="$BUNDLE_GEMFILE"
local old_bundle_path="$BUNDLE_PATH"
# Set environment for checks
export GEM_HOME="$site_root/.gem"
export GEM_PATH="$GEM_HOME"
export PATH="$GEM_HOME/bin:$PATH"
export BUNDLE_GEMFILE="$site_root/Gemfile"
export BUNDLE_PATH="$GEM_HOME"
}
4. Site-Specific Server Management
To allow multiple sites to run simultaneously, we need to be careful about which server we stop:
# Kill any running Jekyll processes in this directory only
echo "Stopping any existing Jekyll servers in $(basename "$site_root")..."
pkill -f "jekyll serve.*$site_root" || true
Using the Environment
To use this environment:
- Create a
.port
file in your Jekyll site directory with your desired port number - Run
nix develop
to enter the development environment - Use
jes
to start your Jekyll server - Use
jes-stop
to stop all Jekyll servers - Use
rebuild-gems
if you encounter native extension issues - Use
reset-ruby-env
for a complete environment reset
The Road Ahead
This development environment is just the first step towards a full home-hosting setup. The next phases will include:
- Setting up a NixOS server on recycled hardware
- Configuring Nginx for multiple site hosting
- Implementing SSL/TLS with Let’s Encrypt
- Setting up dynamic DNS for the home IP
- Creating automated deployment pipelines
Conclusion
The journey from GitHub Pages to home hosting is not just about moving servers—it’s about understanding the entire stack and building robust tools that make development and deployment easier. The Jekyll development environment we’ve created solves immediate needs while providing a foundation for future expansion.
In a future post, we’ll look at setting up the NixOS server and configuring it for multiple site hosting. Stay tuned!
AI Analysis
-
Title/Headline Ideas & Filenames:
- Title: Crafting a Multi-Site Jekyll Dev Environment with Nix Flakes
Filename:
multi-site-jekyll-dev-environment-nix-flakes.md
- Title: From GitHub Pages to Home Server: A Nix-Powered Jekyll Workflow
Filename:
github-pages-home-server-nix-jekyll-workflow.md
- Title: Solving Jekyll’s Multi-Site Mayhem: A Developer’s Nix Flake Solution
Filename:
jekyll-multi-site-nix-flake-solution.md
- Title: Building a Reproducible Jekyll Development Setup with Nix and the
jes
Script Filename:reproducible-jekyll-dev-setup-nix-jes-script.md
- Title: Nix for Jekyll: Streamlining Local Development for Multiple Websites
Filename:
nix-for-jekyll-local-development-multiple-websites.md
- Title: Crafting a Multi-Site Jekyll Dev Environment with Nix Flakes
Filename:
- Strengths:
- Practical Problem-Solving: The article addresses a real-world challenge faced by developers working with multiple Jekyll sites (port conflicts, dependency management).
- Reproducibility: Leveraging Nix flakes is a significant strength, as it promotes a highly reproducible and isolated development environment, which is a best practice.
- Clear Motivation: The author clearly outlines the “why” behind creating this environment, linking it to both immediate needs and a longer-term home-hosting goal.
- Code Examples: The inclusion of the Nix flake configuration and snippets of the
jes
script provides concrete implementation details. - Structured Approach: The article is logically structured, moving from the problem statement to the solution components and future plans.
- Weaknesses:
- Assumed Knowledge: The article assumes a high degree of familiarity with Nix, Nix flakes, Ruby, Bundler, and Jekyll internals. A reader without this background would likely find it difficult to follow.
- Incomplete Scripts: While snippets of the
jes
command andcheck_ruby_version
function are provided, they are not complete. This could be frustrating for readers trying to implement the solution directly from the article as presented. (e.g.,current_dir
injes
is not defined in the snippet, and the script logic is truncated). - Niche Focus: The specific combination of Nix for Jekyll development, while powerful, caters to a relatively niche audience within the broader web development community.
- AI Opinion: This article provides a valuable, albeit highly technical, walkthrough of creating a sophisticated local development environment for managing multiple Jekyll websites using Nix flakes. For developers already versed in Nix and facing the described challenges with Jekyll, this content is likely very useful and offers a robust solution. The clarity is high for its intended audience, but the steep learning curve of Nix means it’s not easily accessible to beginners or those unfamiliar with declarative package management. The article serves well as a technical log or a blueprint for a specific problem, demonstrating a practical application of Nix for web development workflows. Its potential for a book chapter is strong, provided it’s expanded with more complete examples and perhaps some foundational explanations of Nix for those less familiar.