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
      
  • 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 running bundle 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.

  1. 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
      ---
      
  2. 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 replace site.categories.coding with site.categories.psychology.
  3. 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.

  1. 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
    
  2. 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 as myscript.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.

  1. Start the local Jekyll server:
    # In your blog's directory
    bundle exec jekyll serve --port 4001
    
  2. 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
    
  3. 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.

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

  1. 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
    *~
    
  2. 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”.
  3. 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

  1. 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.
  2. 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.
  3. 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.
  4. 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.

  5. 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

  1. Domain in Cloudflare: You have successfully purchased your domain name through Cloudflare Registrar.
  2. 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

  1. Log in to your Netlify account and go to the dashboard for your site.
  2. Navigate to Site configuration > Domain management.
  3. Click Add a domain (or Add custom domain).
  4. Enter your custom domain name (e.g., yourdomain.com) and click Verify.
  5. Netlify will detect that the domain is already managed elsewhere. Click Add domain.
  6. 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 the www subdomain pointing to your Netlify site address (your-awesome-site.netlify.app). Keep this page open.

Step 2: Configure DNS Records in Cloudflare

  1. Log in to your Cloudflare account and select your domain.
  2. On the left sidebar, click on DNS.
  3. You may need to delete any existing placeholder A, AAAA, or CNAME records that would conflict with the new ones.

  4. 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.
  5. 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.

  1. In your Cloudflare dashboard, go to SSL/TLS > Overview.
  2. 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

  1. Return to your Netlify domain management page. DNS changes can take a few minutes to a few hours to propagate.
  2. Netlify will automatically detect the new records and begin provisioning a free Let’s Encrypt SSL certificate for your domain.
  3. 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:

  1. Write a new post in Markdown.
  2. Preview it locally with bundle exec jekyll serve.
  3. 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.