bashblog: A Whole Blog Engine in One Bash Script
Most blog engines want a database, a runtime, a package manager, and a list of dependencies longer than the content you're trying to publish. Carlos Fenollosa went the other direction and built bashblog: a complete static blog generator that lives in a single Bash script of roughly a thousand lines, runs on base system utilities, and needs no installation at all. You drop bb.sh into a public web folder, type ./bb.sh post, and start writing.
It's worth a look not because you'll necessarily run it, but because it's a clean demonstration of how little you actually need to publish a blog — and a useful reference point the next time a "simple" tool wants you to install forty npm packages.
The Whole Thing, in One File
The pitch is genuinely minimal:
- No installation. Download
bb.sh,chmod +xit, run it. - Zero dependencies. It runs on base utilities —
date,basename,grep,sed,head, and friends. Nothing toapt install. - Portable across Unixes. GNU/Linux, BSD, and macOS out of the box. It autodetects which command switches it needs per platform, so it doesn't assume GNU coreutils on a Mac.
- Everything static. All it produces is HTML. You only need shell access to a machine with a public web folder.
The basic workflow is three commands. Run it bare to see what's available:
./bb.sh
If it isn't executable yet:
chmod +x bb.sh
Then write a post — it uses Markdown if a Markdown processor is installed, or you can force HTML:
./bb.sh post
./bb.sh post -html
The script handles the rest: it generates the post page (using the title as the URL), rebuilds the index, regenerates per-tag pages, and updates the RSS feed. Visit the public URL for that folder and the new post is there.
What It Actually Does
For something this small, the feature list is surprisingly complete:
- Markdown support (via a third-party processor like Gruber's
Markdown.pl) - Drafts you can save and resume later, plus a post preview
- One HTML page per post, titled from the post's headline
- A configurable number of posts on the front page
- Automatic RSS generation
- An all-posts index page and automatic per-tag pages
- A "rebuild everything" command that regenerates all files while preserving your source data
- A built-in stylesheet so it's readable by default
- Headers, footers, and extra-content hooks for banners or images
- An automatic site backup (
.backup.tar.gz) every time you post
Configuration is optional for a test drive. When you do want to set your blog title, author, and the rest, you have two choices: edit the global_variables() function inside bb.sh, or — better, if you want to keep pulling updates from git without merge conflicts — drop a .config file alongside it. The script loads its in-script defaults first, then overlays anything in .config, so you only override what you need.
⚠️ One formatting gotcha the docs call out and that bites people: in the .config file, quote your values, don't prefix variable names with $, and don't put spaces around the =. It's shell variable-assignment syntax, so title="My Blog" works and title = My Blog doesn't. Standard Bash assignment rules, but easy to forget when it looks like a config file.
It uses your $EDITOR to open posts, so it drops you straight into whatever you already use.
Why a Static Bash Script Is a Security Win
Here's the part worth dwelling on for anyone running their own infrastructure. The thing bashblog doesn't have is the thing that makes it interesting: no database, no PHP, no application server, no admin login page, no plugin system.
That's a dramatically smaller attack surface than a typical dynamic CMS. There's no SQL injection vector because there's no SQL. There's no vulnerable admin panel exposed to the internet because the "admin interface" is your shell on the box. There's no plugin ecosystem to ship you a compromised dependency. The public-facing artifact is plain static HTML — the web server just serves files, and an attacker hitting your blog finds nothing to exploit but a directory of .html. Compare that to the steady drumbeat of CMS plugin CVEs and credential-stuffing against /wp-admin, and the static approach starts looking less like a toy and more like a deliberate hardening choice.
This is the same reasoning behind a lot of the self-hosting tradeoffs worth thinking through — owning your stack means owning its attack surface too, which I got into in the hidden security risks of self-hosting everything. A static generator sidesteps a whole category of those risks by simply not running any dynamic code in the request path.
⚠️ That said, static doesn't mean zero-maintenance from a security standpoint. The box still needs a patched web server and a locked-down shell, since shell access is the publishing mechanism — compromise the account that runs bb.sh and the attacker can rewrite every page. Treat the publishing account like the privileged thing it is: key-only SSH, no password auth, and ideally not the same account serving other things. The blog content is static; the path to publishing it is not.
Where It Fits
bashblog is squarely a tinkerer's tool, and it's honest about that. It's perfect if you want a personal blog with no moving parts, you're comfortable in a terminal, and you'd rather understand your entire publishing pipeline than outsource it to a platform. The single-script design means you can actually read the whole thing in an afternoon and know exactly what it does — a rare luxury.
It's the wrong tool if you want a polished editing UI, multiple non-technical authors, or rich theming and a plugin ecosystem. For that you'd reach for a heavier static generator (Hugo, Eleventy) or a full CMS, and accept the larger surface that comes with it. There's also a practical limit at scale — regenerating thousands of posts by rebuilding flat files gets slow in a way a "just serve files" model doesn't fully escape, a tradeoff that shows up in any system swapping a database for plain files, which I poked at in teaching a bucket to speak git.
But for the stated goal — a dead-simple way to publish to a public folder with nothing but shell access — it nails it. Credit to Carlos Fenollosa for proving that "a blog engine" and "a thousand lines of Bash" aren't mutually exclusive, and for keeping it maintained and readable instead of letting it sprawl.
If nothing else, it's a healthy reminder of how much complexity we accept by default for problems that don't actually require it.