McGarrah Technical Blog

Jekyll Run Plugin: Local Development Settings That Actually Work

· 9 min read

The Jekyll Run VS Code extension (Dedsec727.jekyll-run) gives you a one-click button to build and serve your Jekyll site. It works well for basic use, but if you write future-dated posts, use drafts, or run a multi-root workspace, the defaults will bite you.

This post covers the configuration I use, the settings precedence that tripped me up, and a bash script fallback for when the extension gets confused.

Why Jekyll Run

Without the extension, local development means opening a terminal and running:

bundle exec jekyll serve --trace --drafts --future --unpublished --livereload --incremental

Jekyll Run wraps this into a button in the VS Code status bar. Click to start, click to stop. It picks up your workspace settings for command-line arguments and handles the process lifecycle.

For a blog with 130+ posts where I’m constantly previewing drafts and future-dated articles, the convenience is worth the occasional quirk.

Developer experience is a platform engineering concern, whether you’re standardizing IDE settings across a 50-person engineering organization or configuring a Jekyll plugin for a solo blog. The time lost to tooling friction — a missing CLI flag, a settings precedence conflict, a stale incremental build — compounds. Getting the local development environment right once means every future writing session starts clean.

The Flags That Matter

Here’s what each flag does and why I use all of them:

Flag Purpose
--trace Show full Ruby backtraces on errors instead of cryptic one-liners
--drafts Render files in _drafts/ as if they were published
--future Include posts with dates in the future
--unpublished Render posts with published: false in front matter
--livereload Auto-refresh the browser when files change
--incremental Only rebuild changed pages (faster, but occasionally stale)

The critical ones are --future and --drafts. Without them, you can’t preview the content you’re actively writing.

Configuring the Extension

The extension reads its command-line arguments from the jekyll-run.commandLineArguments setting. Add this to your workspace’s .vscode/settings.json:

{
    "jekyll-run.commandLineArguments": "--trace --drafts --future --unpublished --livereload --incremental",
    "jekyll-run.stopServerOnExit": true
}

The stopServerOnExit setting kills the Jekyll process when you close VS Code, preventing orphaned processes that block port 4000 on the next launch.

Settings Precedence: Where It Gets Tricky

VS Code settings come from multiple sources, and higher-priority settings silently override lower ones. For the Jekyll Run extension, the precedence is:

  1. Multi-root workspace file (.code-workspace) — highest priority
  2. Workspace folder settings (.vscode/settings.json)
  3. User settings (~/.vscode-server/data/User/settings.json)
  4. Machine settings (~/.vscode-server/data/Machine/settings.json)

The Multi-Root Workspace Trap

If you use a multi-root workspace (multiple folders in one VS Code window), the .code-workspace file’s settings override per-folder .vscode/settings.json files. An empty settings block in the workspace file:

{
    "folders": [
        { "path": "my-blog" },
        { "path": "resume" }
    ],
    "settings": {}
}

…means the extension falls through to machine or user settings, which may not have your flags. The fix is to add the settings explicitly:

{
    "folders": [
        { "path": "my-blog" },
        { "path": "resume" }
    ],
    "settings": {
        "jekyll-run.commandLineArguments": "--trace --drafts --future --unpublished --livereload --incremental",
        "jekyll-run.stopServerOnExit": true
    }
}

macOS users: If you see TypeError: Cannot read properties of null (reading 'toString') in a multi-root workspace, the problem is deeper than settings — it’s related to macOS GUI PATH inheritance and Ruby version management. See Jekyll Run Plugin: Fixing the Multi-Root Workspace Crash for the full diagnosis and fix.

The Machine Settings Trap

On VS Code Remote (SSH, WSL, etc.), machine settings live at ~/.vscode-server/data/Machine/settings.json on the remote host. If someone — or an extension update — writes a jekyll-run.commandLineArguments value there with fewer flags, it can override your workspace settings depending on how the extension resolves precedence.

Check what’s actually there:

cat ~/.vscode-server/data/Machine/settings.json

Diagnosing Which Settings Win

The fastest way to confirm what the extension is actually using:

ps aux | grep jekyll | grep -v grep

This shows the exact command line. If you see --trace --drafts but not --future, your workspace settings aren’t being picked up.

The _config.yml Trap

Even with --future in your CLI flags, Jekyll will ignore it if _config.yml explicitly sets:

future: false

The config file value overrides the command-line flag. This is counterintuitive — you’d expect CLI flags to win — but that’s how Jekyll works.

The fix is to not set future in _config.yml at all. The default is false, which means:

# Don't do this — it overrides the --future CLI flag
# future: false

# Do this — let the CLI flag control it
# (just leave it out entirely, or comment it)

This applies to show_drafts and unpublished as well. If you set them explicitly in _config.yml, the CLI flags become useless.

The Draft and Unpublished Visibility Trap

This one cost me hours. The --drafts and --unpublished flags make Jekyll render draft files and posts with published: false — but they don’t appear in site.posts. That means:

The files exist in _site/. Jekyll built them. But any template that iterates site.posts (which is nearly every listing page) silently excludes them.

How to Confirm They Exist

Check the build output directly:

ls _site/your-draft-slug/

If index.html is there, Jekyll rendered it. You can view it at http://127.0.0.1:4000/your-draft-slug/. It just won’t appear in any listing.

Draft Sorting: They’re at the Bottom, Not the Top

Even when drafts do appear in site.posts, they show up at the end of the archive — after your oldest post. You’ll scroll past 100+ articles looking for your new draft at the top and conclude it’s missing.

Jekyll appends drafts after all regular posts in site.posts rather than sorting them into chronological position by their assigned date. So a draft dated 2026-05-10 appears after a post from 2001, not at the top of the list where you’d expect it.

Check the bottom of your archive page, not the top.

The Workaround

Set published: true in your draft’s front matter. The file is still in _drafts/ so it won’t deploy to production (GitHub Actions doesn’t use --drafts), but locally it will appear in all listings.

The published: false flag is useful for posts in _posts/ that you want to temporarily hide. For files in _drafts/, it’s counterproductive — you’re already using the drafts folder to prevent publication, and adding published: false makes them invisible even in local development.

The Bash Script Fallback

The Jekyll Run extension occasionally gets into a bad state — it thinks a server is running when nothing is on port 4000. When that happens, I fall back to a bash script:

#!/bin/bash
bundle exec jekyll serve --trace --drafts --future --unpublished --livereload --incremental

Save this as start-jekyll.sh in your project root. When the extension misbehaves:

  1. Try Jekyll Run: Stop Server from the Command Palette (Ctrl+Shift+P)
  2. If that doesn’t work, run Developer: Reload Window
  3. If that doesn’t work, use the script: bash start-jekyll.sh

Clearing Stale State

If you’re getting build errors that don’t match your current code, incremental builds may be serving cached content. Clear everything:

rm -rf .jekyll-cache _site && bash start-jekyll.sh

This forces a full rebuild from scratch.

My Complete Setup

For reference, here’s every file involved in my local development configuration:

.vscode/settings.json (per workspace folder):

{
    "jekyll-run.commandLineArguments": "--trace --drafts --future --unpublished --livereload --incremental",
    "jekyll-run.stopServerOnExit": true
}

blog-workspace.code-workspace (multi-root workspace):

{
    "folders": [
        { "path": "mcgarrah.github.io" },
        { "path": "resume" }
    ],
    "settings": {
        "jekyll-run.commandLineArguments": "--trace --drafts --future --unpublished --livereload --incremental",
        "jekyll-run.stopServerOnExit": true
    }
}

start-jekyll.sh (fallback script):

#!/bin/bash
bundle exec jekyll serve --trace --drafts --future --unpublished --livereload --incremental

_config.yml (no explicit future setting):

# Future posts controlled by CLI --future flag
# Default is false, so production builds exclude future posts
# Local development uses --future to preview them

References


About the Author: Michael McGarrah is a Cloud Architect with 25+ years in enterprise infrastructure, machine learning, and system administration. He holds an M.S. in Computer Science (AI/ML) from Georgia Tech and a B.S. in Computer Science from NC State University, and is currently pursuing an Executive MBA at UNC Wilmington. LinkedIn · GitHub · ORCID · Google Scholar · Resume