Skip to content


Repository files navigation


A straightforward, file-based CMS, optimized for chronological content like podcasts, blogging, comics etc. Currently in alpha state, but I use it for my homepage

Look at the example deployment here, and check out some example themes here.


Bumbler tries to take the ease-of-use of a CMS and combines it with the performance and elegance of a static site generator.

Historically CMSes are easy for users to use, but complex. This complexity can lead to performance issues, security problems, and maintenance headaches. Static site generators are powerful, and performant at runtime, but you have to be a developer to use them, and require a lot of "fiddling". Maintaining your own front matter boilerplate scheme, worrying about files being referenced correctly, and generating an atom feed can be a pain.


admin screenshot



  • Builds to static files for MAXSPEED, portability, and easy administration
  • No arcane front-matter magic, directory structure, or commandline wizardry required
  • Batteries included. Includes support for
    • tags
    • custom pages
    • post attachments and featured images
    • static file linking
    • zero configuration Atom feed
    • easy theming
    • favicon generation
  • Two ways to publish:
    1. Self-host and instantly publish from anywhere, anytime
    2. Write locally and push to github-pages, Neocities, or any other system


These are things that Bumbler intentionally lacks and will likely never support:

  • compiling stylesheets for you
  • multi-user login
  • nested page templates
  • in-page editing tools
  • built-in social features (comments etc)
  • built-in analytics
  • file conversion (video transcoding, image resizing etc. Favicons are an exception because they're a huge pain.)
  • built-in search

User guide


step 0: install Node.js and NPM

  1. Create a new folder for your site and open a terminal inside it
  2. npm install -g bumbler
    • Windows Users: You will need to either enable "Developer Mode" (windows 10 only), or run the following commands as administrator, so that the symlinks can be created.
  3. Run bumbler init and follow the directions
  4. Run bumbler to start up your site
  5. Go to localhost:3000/admin to start making posts and customizing your site.

  1. Try running bumbler --help to see other options, like changing the port. Bumbler comes with some utility commands for generating an nginx config etc.


assets/        # a symlink to ./_bumblersrc/assets, where uploads go
atom/          # your atom feed and its paginated pages will go here
page/          # your site's paginated pages get built to here
post/          # each post gets its own page in here
tag/           # each tag gets an index and paginated pages in here
_bumblersrc/   # where the site source data is held
  ║  #     Things the UI will manage for you:# ┌───────────────────┴──────────────────┐
  ╠═bumbler.json  # site settings             │
  ╠═posts/        # Your post data lives here │
  ╠═pages/        # custom pages              │
  ╠═assets/       # file uploads go here      │
  ╠═layout.pug    # the site template         |# └──────────────────────────────────────┘
  ║  #         Modify these manually if you want:# ┌─────────────────────┴────────────────────────────────────────────────┐
  ╠═static/      # files & directories here will be mirrored to the site root │
  ╠═favicon.png  # Put an icon here and favicon.ico will be built             |
  ╠═plugins/     # plugins go in here                                         |
  ╚═scripts/     # you can put executable scripts in here and bumbler will    |
       |         #                          let you run them from the UI      |
     # └──────────────────────────────────────────────────────────────────────┘

Publishing Guide


If you have an extra $7/month, you can host it on DigitalOcean ($5), with backups ($1), and get a domain ($1). Here are some tips:

  • Bumbler will not work on Heroku or other SAAS platforms with an ephemeral filesystem, because of Bumbler's filesystem-based nature. All of your files and settings will get blown away every 24 hours or so.
  • Install a newer nodejs version using the instructions here:
  • Use HTTPS
  • Set up backups in case your site explodes, or hackers get in and ruin everything.

Static Hosting

Since the site builds to static files, you can easily build your site on your PC, then drop the files on a static host using SSH or SFTP. I use github pages for One caveat is that bumbler relies on symlinks, so make sure your host supports those.

Developer Guide

Developing Plugins

Bumbler has a plugin system!

Plugins should go in _bumblersrc/plugins/.

Your plugin gets passed two parameters: the plugin library and a ready callback.

You must call the ready callback with any errors and any hooks you want to handle.

Here's a simple example with hooks:

// _bumblersrc/plugins/last-updated.js

// Put a generationDate value on every page when the site builds

module.exports = function(pluginLib,pluginReady){

  var hooks = {
      var page = JSON.parse(JSON.stringify(oldPage));
      page.generationDate = new Date().toISOString();

  // we're ready right away

Here's a more complex example of how you could use Bumbler to publish some other feed's data:

// _bumblersrc/plugins/feed-subscriber/index.js

const subscribe = require('theoretical-atom-subscription-library');

module.exports = function(pluginLib,pluginReady){

    var postData = {title:item.title,caption:item.caption};

  // this plugin uses no hooks
  var hooks = {};

  // we're ready right away

Available hooks

beforePageRender: function(existingPageData,done)
  => you must call done(error,modifiedData)

pluginLib methods

  => add a post, or modify it by its ID.  Use this to create new posts from another source.

  => rebuild the site pages if you add a new post, or any other reason.

Developing Bumbler itself

The easiest way to develop is in one terminal run gulp watch and in another terminal run cli.js --dir test/kitties.

Building is handled by Gulp. There are a few commands you can run:

  • gulp builds bumbler for development
  • gulp dist builds bumbler for distribution / production
  • gulp watch builds bumbler for development and will build it again whenever files change

This project compiles pug to html, scss to css, and js to js via browserify + babel. All built source files are located in dev-src/

Theming Guide

Each page gets the following passed to the template:

  _:"the Lodash library",
    authorName:"Your Name",
    title:"Site Title",
    description: 'Site Description goes here.',
    avatar: '../../your/avatar.png',
    pageCount:10, // total number of pages (if this is a tag page, this will be set to the number of pages for this tag)
    postsPerPage:10, // as configured in settings
    tags:[ // all tags used on the site
      content:"<p>This is some custom content</p>"
        caption:'<p>testing my new camera</p>',
        date:new Date('October 30, 2017'),
        englishDate:'October 30, 2017',
        title:'fruit photos',
            widget:'<img src="/assets/photo.jpeg"></img>',
            featured:true|false, // the UI has an option to "feature" assets, which sets this flag to true.
            inine:true|false // whether the asset was included in post caption


linkTo is a utility function that will give you links to the various URLs of the site. These are the valid uses of the linkTo function:

linkTo('feed')// the atom feed
linkTo('page',3)// third page of the whole site
linkTo('tag','apples')// apples tag
linkTo('tag','apples',2)// second page of the apples tag
linkTo('admin')// admin login page

_ is Lodash.

The landing page will have page.isIndex == true.

Custom pages have page.isCustom === true and a page.customPage object.

Individual post pages have !page.number, page.posts and page.posts.length === 1.

Pages with multiple posts have a page.number, 0 or more page.posts, and some page.links.

Tag pages have a page.tag and a page.number.

Basic Theme

The most barebones, functional theme will look like this. Notice how you need to parse the page and site objects defensively:

    title= site.title
    link(type="application/atom+xml", rel="alternate", href=linkTo("feed"))
    meta(name="description" content=site.description)

    if page.isCustom
      h1= page.customPage.title
      div!= page.customPage.content

    if page.posts
      each post in page.posts
        h1= post.title
        div Posted in these tags:
          if post.tags
            each tagName in post.tags
              a(href=linkTo('tag',tagName))= tagName
        each asset in post.assets
          div!= asset.widget
        div!= post.caption


    if page.links
      if page.links.previousPage
        a(href=page.links.previousPage) view previous page
      if page.links.nextPage
        a(href=page.links.nextPage) view next page

    div Copyright #{new Date().getFullYear()} #{site.authorName}


No releases published


No packages published