Table Of Contents

August 11, 2026 · View on GitHub

Introduction

markdown-serve is a cross-platform command-line web server that serves a directory of Markdown files as browsable HTML. Just point it at a directory, it lists the .md files as a collapsible tree, and renders them in the browser when clicked.

I prefer vi/vim in a terminal over VS Code and such for writing Markdown, and like to see how the output is rendered in the browser. The server watches for file changes by default and reloads the browser automatically via Server-sent events.

I find it much more pleasurable to work that way. Hope you find it useful as well.

Suggestions, pull requests are welcome but please keep in mind that I like to keep things simple.

Latest Version (v1.0.5)

The latest version is v1.0.5, released on xdtae . Please look at ChangeLog.md for details.

Features

  • Lists .md files as a collapsible tree (directories and files)
  • Renders Markdown as clean HTML
  • Syntax highlighting via highlight.js loaded from CDN
  • GitHub-flavored Markdown extensions: tables, strikethrough, task lists, fenced code blocks, auto-heading IDs
  • Live reload via Server-sent events when files change on disk (on by default)
  • Recursive directory support with empty directory pruning
  • Path traversal protection
  • Binds to 0.0.0.0 by default so you can access it remotely
  • Dark mode via the -dark flag
  • mdsr.sh/mdsr.ps1 helper scripts to restart markdown-serve without manually killing a running instance first

Installation

Download pre-compiled binaries

Download a pre-built binary for your platform from Releases page.

Extract the archive and copy the binary as markdown-serve (Linux/macOS) or markdown-serve.exe (Windows) to somewhere in your PATH.

Installing using Homebrew on Mac/Linux

You will need to install Homebrew first.

Install

First install the custom tap, then trust it. Homebrew 6.0+ refuses to load formulae from third-party taps until they are explicitly trusted.

brew tap muquit/markdown-serve https://github.com/muquit/markdown-serve.git
brew trust muquit/markdown-serve
brew install markdown-serve

Or tap, trust and install in one go:

brew tap muquit/markdown-serve https://github.com/muquit/markdown-serve.git
brew trust muquit/markdown-serve
brew install muquit/markdown-serve/markdown-serve

Upgrade

brew upgrade markdown-serve

Uninstall

brew uninstall markdown-serve

Remove the tap

brew untap muquit/markdown-serve

Building from source

Make sure go is installed.

git clone https://github.com/muquit/markdown-serve.git
cd markdown-serve
go build .

or Look at Makefile and type:

make

Requires go-xbuild-go for compiling cross-platform binaries

Usage

  • Start markdown-serve server with a directory containing .md file
  • Point your browser to the URL e.g. http://localhost:8485
  • Edit Markdown files and the changes will be automatically refreshed in your browser
➤ markdown-serve -h
Usage of markdown-serve:
  -dark
    	Render pages in dark mode
  -host string
    	Host to bind to (default "0.0.0.0")
  -port int
    	Port to listen on (default 8485)
  -version
    	Print version and exit
  -watch
    	Reload browser on file changes (default true)

If no directory is given, the current directory is used.

Options

OptionDefaultDescription
-darkRender pages in dark mode
-host0.0.0.0Host to bind to
-port8485Port to listen on
-versionPrint version and exit
-watchtrueReload browser on file changes

Examples

Serve the current directory:

markdown-serve

Serve a specific directory on a custom port:

markdown-serve -port 8485 ~/notes

Serve without live reload:

markdown-serve -watch=false /path/to/docs

Restrict to localhost only:

markdown-serve -host 127.0.0.1

Serve in dark mode:

markdown-serve -dark ~/notes

Helper CLIs (mdsr.sh and mdsr.ps1)

markdown-serve doesn't track whether another instance is already running. Start a second one on the same port and it just fails with address already in use. To avoid having to manually find and kill the running process before starting a new one, two small wrapper scripts are included:

  • mdsr.sh for macOS/Linux
  • mdsr.ps1 for Windows (PowerShell)

Both take exactly the same arguments as markdown-serve itself. Before starting a new instance, they look for an already running markdown-serve process, print what it was serving, kill it, and then start the new one in its place.

NOTE: By default, the scripts looks for markdown-serve and markdown-serve.exe in Linux/macOS and Windows respectively. Read below for more info.

Usage

The scripts accept the same options and directory argument as markdown-serve. Copy them somewhere in your PATH.

macOS/Linux:

mdsr.sh -port 8485 ~/notes

Windows (PowerShell):

mdsr.ps1 -port 8485 C:\notes

Serve in dark mode:

mdsr.sh -dark ~/notes

If markdown-serve was already running, you'll see something like:

Killing running markdown-serve (pid 79299): markdown-serve /Users/muquit/notes
Starting: /Users/muquit/bin/markdown-serve -port 8485 /Users/muquit/notes

mdsr is short for markdown serve restart. Easier to type than the full name when running it often.

The scripts locate markdown-serve on PATH. To point at a binary that isn't on PATH, set the MARKDOWN_SERVE_BIN environment variable. Both scripts respect it:

MARKDOWN_SERVE_BIN=/path/to/markdown-serve mdsr.sh ~/notes
$env:MARKDOWN_SERVE_BIN = "C:\path\to\markdown-serve.exe"
mdsr.ps1 C:\notes

Accessing Home Network from anywhere

Whenever needed, I run markdown-serve on a machine at home and access it from anywhere over Tailscale using a browser to see how the Markdown is rendered as HTML. As long as both devices are on the same Tailscale network, it just works. Browse and edit Markdown files remotely as if I were sitting at home.

Tailscale

Tailscale is a zero-config VPN built on WireGuard that creates a secure, end-to-end encrypted mesh network between devices. Unlike traditional VPNs that route all traffic through a central server, Tailscale connects devices directly to each other (peer-to-peer) whenever possible, which makes it extremely fast with minimal latency. There is nothing to configure: no port forwarding, no dynamic DNS, no firewall rules.

Note: I am not affiliated with Tailscale in any way, just a satisfied user.

Live Reload

When -watch is enabled (the default), the server uses github.com/fsnotify/fsnotify to watch the served directory tree for changes. When a .md file is written, the browser reloads automatically via a Server-sent events connection at /events. No WebSocket or external tooling is needed.

To disable live reload:

markdown-serve -watch=false

Dependencies

The project uses the go standard library for HTTP serving.

Screenshots

Listing of all Markdown files:

alt markdown-server ss1

Clicked on the README.md

alt markdown-server ss2

See Also

markdown-toc-go - markdown TOC generator with glossary placeholder expander

License

MIT. Look at LICENSE.txt for details.

Author

Built with the help from Claude Code. Look at CLAUDE.md for the prompt used for implementation.


TOC is created by https://github.com/muquit/markdown-toc-go on Aug-10-2026