This blog now 1 uses Jekyll - a static blog generator that takes markdown as an input and pumps html as output. I then copy it over to my hosting server - Firebase, which then happily serves it to the interwebs.

This setup has worked out swimmingly well thus far and has been rock solid. Even when my last post got high up the ranks of HN, my blog held steady. Not even the slightest of fears or signs of being fireballed or slashdotted. This is the beauty of HTML.

To achieve the above, these are the actual steps:

  1. write my blog post in markdown
  2. git commit and push blog post to git(hub) (this entire blog is version controlled)
  3. run command “bundle exec jekyll build” (use jekyll to generate the html)
  4. run command “bundle exec htmlproofer ./site –check-favicon –check-html” (on generated html from step 3 to make sure I don’t have broken links etc.)
  5. run command “firebase deploy –only hosting”

That’s 3 steps more than my lazy self is willing to do every single time.

In software land, we’ve solved this problem with CD (Continuous Delivery). You have a server somewhere on the internet, that keeps checking your github repo every time you’ve made a change, then runs a sequence of commands for you.

That’s exactly what I did for this blog. I now use Circle CI 2 to ping github and run through steps 3-5. If all’s well my blog is updated with my new post. If something didn’t go well, I get an email notifying me about it which I can then promptly fix.

Even when I’m just tweaking the appearance of this blog, I tend to make a lot of changes 3 to my css (it’s like my happy place). Setting up CD has saved me huge amounts of time! If you’re a developer, or savvy enough to dabble with some code, I highly recommend it!

Here’s what my circle ci config looks like:


version: 2
jobs:
  build:

    working_directory: ~/repo

    # docker hub images https://hub.docker.com/
    docker:
      - image: kaushikgopal/ruby-node:2.6.5_12.14.0

    steps:
      - checkout

      # Download and cache dependencies
      - restore_cache:
          keys:
          # Find a cache corresponding to this specific file's checksum
          # when any of these files are changed, this key will fail
          - v00-deps
          # Find the most recently generated cache used from any branch
          - v00-deps-

      - run:
          name: Ruby dependencies
          command: bundle install --path vendor/bundle --verbose

      - run:
          name: Node dependencies
          command: npm install [email protected] --unsafe-perm --verbose

      - save_cache:
          # path for directories is relative
          # to the working_directory of your job
          paths:
            - vendor/bundle
            - node_modules
          key: v00-deps

      - run:
          name: Build
          command: bundle exec jekyll build --verbose

      - run:
          name: HTMLProofer tests
          command: bundle exec htmlproofer $HOME/repo/_site \
                        --allow-hash-href \
                        --check-favicon \
                        --check-html

      - run:
         name: Deploy
         command: $HOME/repo/node_modules/firebase-tools/lib/bin/firebase.js\
                        deploy \
                            --token=$FIREBASE_TOKEN \
                            --non-interactive \
                            --only hosting

To get the right version of ruby and node playing well together on the Circle CI server, I even had to create my custom docker container. I chatted in great detail about this on Fragmented episode 188.

Curious to know more about this setup? hit me up on Twitter and I’ll gladly try to help.



  1. I started out with Blogger, moved to Wordpress.com, then Wordpress.org, then Octopress which i deeply loved, and finally now to Jekyll↩︎

  2. They have a generous free tier. ↩︎

  3. Did you notice the recent switch to FF Tisa and Jetbrains Mono 😍 ? ↩︎