Publishing at Home: Ghost, Photo-Stream, and Static Sites

I didn't have a good photo for "publishing," so here's the Vancouver Public Library Central

In addition to the self-hosted services for media consumption, I also self-host some tools for publishing - Ghost, Photo-Stream, and serving some static content


This blog runs on the Ghost platform. I've used a ton of different blogging platforms over the years, from writing "blog posts" manually in HTML, b2 and b2evolution, of course Wordpress, and others I'm sure I've forgotten about. The past 10 years or so have been mostly non-blogging years - Wordpress has been the dominant platform available, and when I've looked for more-secure, less-bloated alternatives, they've often not had the features I want in a blog publishing tool. The last blog I had that I actually kept up with was on Wordpress, but since I moved all my publishing to a home server environment, when I decided I wanted to start blogging again I didn't want to deal with its security implications (even though I could isolate it on the network to prevent most of them).

I decided to give Ghost another try, as it seems to have come a long way in terms of features since I last looked at it. The writing experience in Ghost is very pleasant - the automatic creation of embedded content is nice, management of metadata for different social platforms and search engines is a nice-to-have, and there are some decent customization features - although some customization is surprisingly difficult, which I'll get to in a bit. On a per-post or per-theme level, you can add custom code to the header and footer of the site, and injecting code in general is quite easy from the admin panel. The theming language seems relatively simple to customize as well.

Unfortunately, this doesn't extend to the Javascript and CSS. Like many projects, Ghost inexplicably requires compiling these resources, which probably saves a massive 3KB per page load or something. I understand this being an option - Ghost is designed for very high-volume websites as a replacement for SaaS like Medium and Substack - but it's very frustrating as the operator of a small site. I have to hack in changes and hope they're not wiped in an update. Similarly, Ghost really, really, really wants to run on bare metal. It's undocumented and trial-and-error to determine which changes can be done with immediate effect, which require a container restart, and which might even require a custom Docker image - it wants to be controlled via its CLI tool, which is unfeasible in a Docker environment. Bind-mounting the volumes out to the host and customizing the code there seems to work with most things, but I will try to tweak some variable somewhere and inexplicably nothing happens.

Overall, whenever I'm just writing, Ghost is very pleasant to work with - as long as I paint inside the lines.


I didn't think it was a particularly unusual use-case; I wanted to replace my Instagram account with a self-hosted gallery that displayed a flat, infinitely-scrolling grid of images. I don't need or want user accounts or comments, albums, ratings, or video support. This proved to be very difficult to solve without writing something myself to serve a directory of images - something I've written before for several other sites, but is a real pain to manage. The popular solutions for photo management have long been trying to replace album-based and/or private photo sharing - Picasa and later Google Photos replacements, not Instagram.

To replace that workflow, I found photo-stream and its more-Docker-friendly fork/successor project. While still curiously difficult to update - it still requires a container restart whenever adding images, which requires compiling the entire site from scratch every time, an extremely arduous process that takes at least 20 seconds on my machine - it's at least very stable and easy to understand and customize.

After upgrading to the fork, I did have a major problem with how automatic rotation of images was being handled, which turned out to be a combination in unusual/non-standard EXIF data from Samsung phones and a bug in the library vips. I've been hesitant to do anything with the site since battling with that issue, even adding new photos, but I'm grateful to the time and effort the developer put into resolving this issue for the few of us were impacted.

Static Sites

I don't operate very many static sites, but I can't see any reason to migrate things like my portfolio to something that requires any server-side code. While at some point in the future I may end up migrating these to something like a static site generator - for things like the portfolio that aren't updated often, but aren't completely unchanging - or at least to an S3-like public bucket server to even further reduce server resources, the reverse proxy I detailed in an earlier post, Caddy, provides extremely simple serve-from-a-directory static site hosting. As I have for decades now, these static sites are entirely hand-edited in the terminal with nano, so this form of hosting is currently a great fit, and very easy to expand if I need to host any other static sites in the future.

Closing Thoughts

I'd definitely recommend Ghost to anyone looking to publish a blog (or an email newsletter, although I've shared my thoughts in an earlier post about Ghost's email functionality). Photo-Stream I'm less pleased-with, but only because I don't think I'm quite using it "correctly" - I just haven't found something that is a better fit. Perhaps both my static sites and my photo stream need to migrate to Hugo or one of the other static site generators to centralize and automate the publishing of both types of content. A project for the future, perhaps.

Jordan Cooks

Jordan Cooks

Jordan listens to too many podcasts, has too many streaming subscriptions, loves dogs, is the Integration Engineer Team Lead at Bitwarden, and makes a mean vegan baked mac and cheeze.
North Bend, OR