Technomancer Tools: Bookstack

03 April 2024

I mentioned not too far back that I'd finished migrating my wiki over to a new piece of software, but it was a little outside of what I'd been trying to accomplish in that post. It seemed a good idea to circle back and explain what I meant by that.

Don't get me wrong, I quite like Pepperminty Wiki. It's a fine piece of software - lightweight, configurable, it uses flat files for storage, and it's nice and snappy. Especially in situations where the web hosting provider is badly over-provisioned and moderately complex web applications tend to bog down. But after a few years of using it I found that I needed a little more. A wiki that was more mobile friendly (and not necessarily through the use of a mobile app), that made uploading files a bit easier (in particular, photographs), and which had a nicer text editor. I like Markdown but when I'm trying to write notes I don't necessarily want to have to micromanage formatting, especially on my phone. Maybe I'm getting a little cranky in my old age 1 but sometimes I just want a WYSIWIG user interface so I can get things done.

I'll skip the parts about experimenting and trying to get a couple of other packages to do what I wanted (because I wrote about that already) and cut to the chase. I wound up going with Bookstack and after about a week of messing around with it I decided that it was the right tool. It was pretty simple to set up and the instructions are straightforward. One thing I'd like to point out is that, while technically shared hosting isn't supported by Bookstack I didn't have any trouble with it. This is partially due to A2 Hosting (note: this is a referral link; if you sign up I get an account credit) having a control panel that lets you pick which PHP extensions are turned on for your account. 2

Unlike a lot of wikis Bookstack doesn't try to be overly abstract when it comes to organizing pages. Bookstack gives you shelves, books, chapters, and pages. Pages are pretty self explanatory. Chapters are collections of pages; chapters are optional, incidentally. Books are collections of chapters and pages; you can have books that are nothing but pages. Shelves are collections of books; I think that shelves are optional. When I was first setting up Bookstack, I created a single book because I needed a book ID for my utility (which wound up being the number 1) and uploaded all of my pages into it. Now, here's the thing: You can stop here if you want. If one book in Bookstack works for you, that's fine. Just because a feature is there doesn't mean that you have to use it. However, I wanted to move stuff into loose categories to at least give me an idea of where to start looking, so I set up a few books and moved pages around. In every book I created a chapter called Archive, which I then moved stuff that I was finished with into (but didn't want to delete). That way, finished projects didn't get in the way of pages that I'm still using often.

One thing that Bookstack has going for it is that you can either go WYSIWYG, or you can edit documents as Markdown. This means that you can either pump Markdown files into a Bookstack installation using the REST API (with the /api/pages API rail and the markdown option), or you can pull your pages out of Bookstack as Markdown documents if you want to (using the /api/pages/{page ID}/export/markdown API rail). So your stuff isn't trapped in Bookstack in a format that you'll have to write a translator for. I wrote a quick hack in Python that uses the corresponding Python module to take a bunch of files and upload them into Bookstack. It's not polished, you'll have to edit the code to configure it, and you'll have to get yourself a pair of API tokens for your setup, per the API documentation linked above. Then you just have to run it with a bunch of .md files:

{17:35:53 @ Sun Mar 24}
[drwho @ windbringer wiki] () $ . bookstack/bin/activate

(bookstack) {17:36:01 @ Sun Mar 24}
[drwho @ windbringer wiki] () $ ./upload_wiki_pages_to_bookstack.py wiki/*.py

Something that I found I had to do when I was done testing was that I had to update the links in all of my pages to point to the 'real' domain that Bookstack now sits on. Thankfully you don't have to do this manually. This is a feature which the Bookstack devs built into the Artisan tool, which comes with every Laravel application and it works very well. I made sure that I had one good backup already (which never hurts) and then used the following command:

{17:39:22 @ Sun Mar 24}
[drwho @ windbringer BookStack] () $ ./artisan bookstack:update-url https://old.wiki.example.com https://new.wiki.example.com

(Not having a trailing slash in the URLs seems important.)

Then to make sure that the search indices and disk cache were up to date, I manually re-indexed my wiki and cleared the cache:

{17:40:20 @ Sun Mar 24}
[drwho @ windbringer BookStack] () $ ./artisan bookstack:regenerate-search

{17:40:44 @ Sun Mar 24}
[drwho @ windbringer BookStack] () $ ./artisan bookstack:regenerate-references

{17:40:51 @ Sun Mar 24}
[drwho @ windbringer BookStack] () $ ./artisan optimize-clear

All of that said Bookstack has been really solid for me. I haven't been this impressed with software in a long while. It's one of the first things I open when I start my browser, and it's been indispensible as a knowledge management system. If I'm figuring out a problem, I come across something interesting, or if I have something that I'd like to throw into a scrapbook of sorts, it's my go-to tool these days. If you're looking into setting up a wiki I'd strongly recommend going to the Bookstack demo site and playing around for an hour or so. You might find yourself pleasantly surprised.


  1. I'm a sysadmin, it comes with the territory. 

  2. In case you're curious, these are the ones I have turned on: bcmath, bz2, calendar, core, ctype, curl, date, dba, dom, enchant, exif, fileinfo, filter, ftp, gd, gettext, hash, zlib, iconv, igbinary, imagick, imap, intl, json, ldap, libxml, mbstring, memcached, mysqlnd, nd_mysqli, nd_pdo_mysql, odbc, opcache, openssl, pcntl, pcre, pdo, pdo_odbc, pdo_pgsql, pdo_sqlite, pgsql, phar, posix, pspell, random, readline, redis, reflection, session, shmop, simplexml, soap, sockets, sodium, spl, sqlite3, standard, sysvmsg, sysvsem, sysvshm, tidy, tokenizer, xml, xmlreader, xmlwriter, xsl