From Localhost to Live & Private: The Ultimate Jekyll Blog Setup Guide
From Localhost to Live & Private: The Ultimate Jekyll Blog Setup Guide
So, you want to start a technical blog. You’re a developer. You don’t want a clunky, database-driven CMS. You want speed, version control, and the ability to write in your favorite editor using Markdown. You want Jekyll.
But setting up a blog is more than just jekyll new
. What happens when posts don’t show up? How do you add downloadable code snippets? And most importantly, what if you want your blog to be private—a personal knowledge base or a pre-launch project accessible only to you and a select few?
This guide covers the entire lifecycle. We’ll go from a blank command line to a fully customized, password-protected Jekyll blog hosted for free. We’ll hit the common roadblocks and solve them, just like in a real-world project.
Part 1: Local Setup - Your Blogging Foundation
First, let’s build the blog on your local machine. This allows you to write, preview, and customize everything before it ever touches the internet.
Step 1: Install the Tools
Jekyll is built with Ruby. You’ll need Ruby and its package manager, RubyGems.
- On macOS (with Homebrew):
brew install ruby
- On Linux (Debian/Ubuntu):
sudo apt update && sudo apt install ruby-full build-essential zlib1g-dev
- On Windows:
Download and run the Ruby+DevKit installer from RubyInstaller for Windows. During installation, accept the default to run
ridk install
on the last step.
Once Ruby is installed, install Jekyll and Bundler (a tool to manage project-specific gems):
gem install jekyll bundler
Step 2: Create and Run Your Blog
Now, let’s create the blog and see it live on your machine.
# 1. Create a new Jekyll site in a folder named "my-personal-blog"
jekyll new my-personal-blog
# 2. Navigate into the new directory
cd my-personal-blog
# 3. Install the specific gems this theme needs
bundle install
# 4. Start the local development server
bundle exec jekyll serve
Open your browser to http://localhost:4000
. You should see your new Jekyll blog with the default Minima theme!
Step 3: Your First Blog Post
Posts live in the _posts
directory. The filename format is crucial: YYYY-MM-DD-your-title-goes-here.md
.
Create a new file: _posts/2025-07-15-my-first-real-post.md
---
layout: post
title: "My First Real Post"
date: 2025-07-15 11:00:00 -0500
categories: getting-started
---
Welcome to my blog! This is content written in **Markdown**.
The section at the top between the `---` lines is called **Front Matter**. It's where you define metadata for the post, like its title and layout.
Save the file. The running server will automatically detect the change and rebuild your site. Refresh your browser, and your new post will appear.
Part 2: Troubleshooting the Inevitable Glitches
Your server is running, but things can go wrong. Here are the most common issues and how to fix them.
- Problem: “My new post isn’t showing up!”
- Cause: Jekyll, by default, will not publish posts with a date set in the future. If you wrote a post today and dated it for tomorrow, it won’t appear.
- Solution: For local development, run the server with the
--future
flag to tell Jekyll to build all posts, regardless of their date.bundle exec jekyll serve --future
- Problem: “Server won’t start!
Address already in use
“- Cause: You have another Jekyll server (or another process) already running on port 4000. This often happens if you closed a terminal window without stopping the server with
Ctrl+C
. - Solution: Start the server on a different port.
bundle exec jekyll serve --port 4001
- Cause: You have another Jekyll server (or another process) already running on port 4000. This often happens if you closed a terminal window without stopping the server with
- Problem: “
Could not locate Gemfile
” or “jekyll: command not found
“- Cause: You are trying to run the command from the wrong directory.
- Solution: Make sure your terminal is inside your blog’s project folder (e.g.,
my-personal-blog
) before runningbundle exec jekyll serve
.
Part 3: Customizing Your Content
A blog is more than just a list of posts. Let’s add sections and downloadable files.
Feature 1: Creating “Coding” and “Psychology” Sections
We’ll use Jekyll’s powerful categories
feature.
- Categorize Your Posts: In the front matter of each post, assign a category.
- For your technical post (
_posts/...-wsl2-guide.md
):--- layout: post title: "Connecting Your MacBook to WSL2" categories: coding tags: [wsl2, vscode, development] ---
- For a future psychology post:
--- layout: post title: "The Psychology of Code Reviews" categories: psychology ---
- For your technical post (
- Create Category Pages: Now, create pages that will list all posts from a specific category.
- Create
coding.md
in your root directory:--- layout: page title: Coding permalink: /coding/ --- # Posts about Coding <ul> {% for post in site.categories.coding %} <li> <h3><a href="{{ post.url | relative_url }}">{{ post.title }}</a></h3> <p>{{ post.excerpt }}</p> </li> {% endfor %} </ul>
- Create
psychology.md
in your root directory with the same content, but replacesite.categories.coding
withsite.categories.psychology
.
- Create
- Update Navigation: Add links to your new pages in
_config.yml
(for the Minima theme). ```yaml header_pages:- about.md
- coding.md
- psychology.md ```
Feature 2: Adding Downloadable Scripts
Let’s say your WSL2 guide has PowerShell scripts that readers should be able to download.
- Organize Your Files: The best practice is to store downloadable assets in the
assets
folder. Create a clear structure.my-personal-blog/ └── assets/ └── downloads/ └── wsl2-automation/ ├── setup-wsl2-ssh.ps1 ├── update-wsl2-ip.ps1 └── connect-wsl2.sh
- Link to the Files in Your Post: In your blog post’s Markdown, simply create a standard link pointing to the file.
### Download the Automation Scripts You can download the complete package of scripts to automate this entire process. * [Download setup-wsl2-ssh.ps1](/assets/downloads/wsl2-automation/setup-wsl2-ssh.ps1) * [Download update-wsl2-ip.ps1](/assets/downloads/wsl2-automation/update-wsl2-ip.ps1) * [Download connect-wsl2.sh](/assets/downloads/wsl2-automation/connect-wsl2.sh)
Jekyll will automatically serve these files, and clicking the link will trigger a download.
Troubleshooting: What If the Downloads Give a 404 Error?
You’ve set everything up, the links are in your post, but clicking them gives a “404 Not Found” error. This is a common hiccup when first setting up downloadable assets in Jekyll. Here are the most likely culprits, from most to least common:
1. The Build Failed Silently
This is the sneakiest and most common issue. Your downloads aren’t working because your entire Jekyll site isn’t building correctly. An error in a completely different file can prevent Jekyll from generating the _site
folder, meaning your assets never get copied to their destination.
- Symptom: The local server crashes or your continuous deployment build fails on Netlify.
- Likely Cause: You have a blog post (especially one about coding, like a Jekyll guide) that contains unescaped Liquid template tags (e.g., ``). Jekyll tries to process this code, leading to an error.
-
The Fix: Wrap any example code that uses Liquid syntax in
and
tags in your Markdown files.This is an example: {{ site.posts | size }}
This tells Jekyll to ignore the code inside, fixing the build error.
2. Filename Mismatch or Case Sensitivity
Your deployment server (like Netlify) runs on Linux, which is case-sensitive. Your local machine (especially Windows) might not be.
- Check for Typos: Ensure the link in your post (
/assets/downloads/wsl2-automation/setup-wsl2-ssh.ps1
) exactly matches the filename in your Git repository. - Check for Case:
MyScript.ps1
is not the same asmyscript.ps1
on the server.
3. How to Test Locally
Before blaming the live server, confirm the files are being served correctly on your local machine.
- Start the local Jekyll server:
# In your blog's directory bundle exec jekyll serve --port 4001
- Use
curl
to check the file headers. This command doesn’t download the file, it just checks if the server can find it.curl -I http://localhost:4001/assets/downloads/wsl2-automation/setup-wsl2-ssh.ps1
- Look for the
HTTP/1.1 200 OK
response.- ✅ If you get
200 OK
, the file is correctly configured in your Jekyll project. The problem is with your live deployment. - ❌ If you get
404 Not Found
, the problem is in your local project structure. Go back and check steps 1 and 2.
- ✅ If you get
By adding this section, you anticipate a common reader frustration and provide them with a clear, actionable debugging guide, making your post even more authoritative and helpful.
Part 4: The Private Web Host
This is the critical step. We want the blog live on the web, but not public. The source code and the live site must be private.
Warning: Do not use standard GitHub Pages. Even with a private repository, a GitHub Pages site is always public.
Our weapon of choice is Netlify, paired with a private GitHub repository. This gives us free hosting, continuous deployment, and robust password protection.
Step 1: Prepare for Deployment with Git
- Create a
.gitignore
file: This file tells Git what not to track. It’s essential for keeping your repository clean. Create a file named.gitignore
in your blog’s root with the following content:# Jekyll _site/ .sass-cache/ .jekyll-cache/ .jekyll-metadata # Ruby / Bundler vendor/bundle/ .bundle/ # OS files .DS_Store Thumbs.db # Logs and temp files *.log *~
- Create a Private GitHub Repository:
- Go to GitHub and create a new repository.
- Give it a name (e.g.,
private-jekyll-blog
). - Crucially, set its visibility to “Private”.
- Push Your Code: In your terminal, from your blog’s directory, run these commands.
git init git add . git commit -m "Initial blog setup with content" git branch -M main # Replace with your actual repo URL git remote add origin https://github.com/your-username/private-jekyll-blog.git git push -u origin main
Your blog’s source code is now securely stored on GitHub.
Step 2: Deploy and Secure with Netlify
- Connect Netlify to GitHub:
- Sign up for a free account at Netlify.com.
- Click “Add new site” -> “Import an existing project” -> “Deploy with GitHub”.
- Authorize Netlify to access your repositories and select your new private repo.
- Configure Build Settings: Netlify will auto-detect that it’s a Jekyll site, but let’s confirm the settings are correct:
- Build command:
bundle exec jekyll build
- Publish directory:
_site
- Click “Deploy site”. Netlify will now pull your code from GitHub and build your site.
- Build command:
- Enable Password Protection (Netlify Identity):
- In your new site’s dashboard on Netlify, go to Site settings > Identity.
- Click Enable Identity.
- Scroll down to “Registration” and set the preference to “Invite only”. This ensures only people you explicitly invite can create an account.
- Go to the Identity tab (in the top navigation) and click “Invite users” to add yourself and anyone else who needs access. They will receive an email to set their password.
- Add the Login Widget to Your Site: To trigger the login modal, you need to add Netlify’s script.
- In your Jekyll project, create the file
_includes/head.html
. - Inside this new file, add the following line:
<script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
This overrides the theme’s default
head
include to add the script.
- In your Jekyll project, create the file
- Commit and Push the Change:
git add _includes/head.html git commit -m "Add Netlify Identity widget" git push
Netlify will automatically detect the push, rebuild your site, and deploy the new version. Now, when anyone visits your site’s URL, they will be greeted by the Netlify Identity login modal. Only invited and registered users can get past it.
Part 5: Connecting Your Custom Domain (from Cloudflare)
Your site is live on a .netlify.app
address, but the final professional touch is using your own domain. This guide shows how to connect a domain managed by Cloudflare to your Netlify site, which is a powerful and common setup.
Prerequisites
- Domain in Cloudflare: You have successfully purchased your domain name through Cloudflare Registrar.
- Site on Netlify: Your Jekyll site is deployed on Netlify and has a default URL like
your-awesome-site.netlify.app
.
Step 1: Get DNS Information from Netlify
- Log in to your Netlify account and go to the dashboard for your site.
- Navigate to Site configuration > Domain management.
- Click Add a domain (or Add custom domain).
- Enter your custom domain name (e.g.,
yourdomain.com
) and click Verify. - Netlify will detect that the domain is already managed elsewhere. Click Add domain.
- Netlify will now show you the DNS records you need to create. It will be an A record for your root domain (
yourdomain.com
) pointing to Netlify’s load balancer IP address (e.g.,75.2.60.5
) and a CNAME record for thewww
subdomain pointing to your Netlify site address (your-awesome-site.netlify.app
). Keep this page open.
Step 2: Configure DNS Records in Cloudflare
- Log in to your Cloudflare account and select your domain.
- On the left sidebar, click on DNS.
-
You may need to delete any existing placeholder
A
,AAAA
, orCNAME
records that would conflict with the new ones. - Create the A Record (for the root domain):
- Click Add record.
- Type:
A
- Name:
@
(This represents your root domain). - IPv4 address:
75.2.60.5
(Always use the IP address shown in your Netlify dashboard). - Proxy status: Leave it as Proxied (orange cloud). This is crucial for enabling Cloudflare’s features.
- Click Save.
- Create the CNAME Record (for the ‘www’ subdomain):
- Click Add record again.
- Type:
CNAME
- Name:
www
- Target:
your-awesome-site.netlify.app
(Use your unique Netlify site URL). - Proxy status: Leave as Proxied (orange cloud).
- Click Save.
Your Cloudflare DNS settings should now look like this:
Type | Name | Content | Proxy Status |
---|---|---|---|
A | @ | 75.2.60.5 | Proxied |
CNAME | www | your-awesome-site.netlify.app | Proxied |
Step 3: Configure SSL/TLS in Cloudflare
This is a critical step to prevent redirect errors.
- In your Cloudflare dashboard, go to SSL/TLS > Overview.
- Set your encryption mode to Full (Strict).
- Why? Netlify provides its own SSL certificate. This setting tells Cloudflare to maintain a secure, encrypted connection all the way to Netlify’s servers.
Step 4: Verification and Finalization
- Return to your Netlify domain management page. DNS changes can take a few minutes to a few hours to propagate.
- Netlify will automatically detect the new records and begin provisioning a free Let’s Encrypt SSL certificate for your domain.
- Once complete, your custom domain will be listed as the primary domain. Your site is now live and secure at
https://yourdomain.com
.
Conclusion: Your Professional Blogging Workflow
You’ve done it. You now have:
- A fast, modern Jekyll blog running locally.
- A clean, organized content structure with categories and downloads.
- A private Git repository for version control.
- A live, password-protected website with free hosting and automatic deployment.
- A professional custom domain from Cloudflare pointing to your site.
Your workflow is now beautifully simple:
- Write a new post in Markdown.
- Preview it locally with
bundle exec jekyll serve
. - Commit and push your changes with
git push
.
Within minutes, your private blog is updated on your custom domain. You have full control, professional-grade security, and a powerful platform to build your knowledge base. Happy blogging!
Frequently Asked Questions (FAQ)
Q: Is Netlify Identity the only way to password-protect my site?
A: For the free tier, Netlify Identity is the most powerful and recommended method. Netlify’s Pro plans offer a simpler site-wide Basic Password Protection, but Identity provides role-based access and is more flexible.
Q: Why is the Full (Strict)
SSL setting in Cloudflare so important?
A: This setting ensures the connection is encrypted end-to-end: from the user’s browser to Cloudflare, and from Cloudflare to Netlify’s servers. If you use Flexible
, the connection from Cloudflare to Netlify is unencrypted (HTTP). This often causes a “redirect loop” error, because Netlify automatically tries to redirect HTTP traffic to secure HTTPS, creating a conflict.
Q: Do I have to pay for anything in this setup?
A: The only mandatory cost is purchasing your domain name from Cloudflare. The entire workflow described—private GitHub repository, Jekyll software, Netlify hosting with continuous deployment, and Netlify Identity for a small number of users—is free. Costs would only be incurred if you exceed Netlify’s generous free tier limits for bandwidth or build minutes.