Hello there. Jekyll is a wonderful tool for building static websites. I like it a lot, but it lacks some kind of CMS. I would love to be able to write posts anywhere, but out-of-the-box, the only way to write something is via git. This method restricts you heavily:

  • It is quite difficult to set up a comfortable workspace on mobile devices, so the only way to write is to use a desktop.
  • Even on a desktop, you need to configure it enough to be able to write posts. I have several desktops, and not every machine is dev-configured. You have to share SSH keys, install Git, install your favorite editor, and so on.

Existing “CMS” solutions

There are some popular solutions, such as jekyll-admin, prose.io, and others. Honestly, I haven’t even tested them on a real website because all of them lack ability to provide comfortable editing on a mobile. They usually provide a web UI, and you have to login there to edit your content. I’m not sure about other features, such as seamless synchronization, drafts, and configuration. Maybe they are okay, but still, in-browser editing on a mobile is a fatal drawback for me.


Notion comes to the rescue. Just imagine being able to manage your Jekyll-powered website with Notion:

  • First-class mobile application and web UI.
  • It’s free.
  • Everything is synchronized out-of-the-box, so you can start writing on mobile and immediately switch to a desktop to continue.
  • And many other features. You probably already know what Notion can do 🌚.

Initially, it may sound difficult to integrate Notion with Jekyll, but it’s actually not (at least after reading this article). There are many tutorials about “how to configure Notion + Jekyll sync”, and there are many ways to do it.

So, Notion + Jekyll

I have come across various approaches on how to do it, and most of them follow a similar pattern: pack the logic (connect to Notion API, access the database, parse content into markdown, etc) into a Docker container, run it using a cron job, and git-commit the output.

In general, I like this approach, but I don’t like the need to manage a large amount of logic, especially if it’s written in a language other than Ruby. Ideally, I want everything to be bundled as a Jekyll plugin with simple configuration via _config.yml.

Luckily, I found a solution that almost meets my requirements: emoriarty/jekyll-notion. The only thing that it does “wrong” for me is that it doesn’t store all the content under git control; it syncs only during the build stage every time. Well, this approach mostly works okay, but there are some drawbacks:

  • It depends on Notion API availability and internet connectivity during the build.
  • It ties your website too much to Notion. If you decide to move to some other CMS, you will need to migrate all your content manually.
  • If you accidentally remove your Notion database, you will lose all your content.
  • If your website’s source code is open-sourced, then it will be incomplete: all the Notion content will be missing.


That’s why seroperson/jekyll-fetch-notion was born (as a result of a pull-request). It’s a fork of jekyll-notion, aimed at synchronizing things separately from the build phase. It introduces a new command, jekyll fetch_notion, which pulls and converts your Notion content according to _config.yml and places it in the appropriate source directory. All you have to do after that is git commit and git push to trigger the build. This plugin still lacks some features, such as custom page fetching and plain data fetching, but it’s a good start nonetheless.

My final setup

So, to configure sync, follow these steps:

  • Create a Notion database by following this guide. If you’re unsure what a database is, read this article.
  • Create a new connection by going to My Integrations. Then copy the given secret.
  • Assign the newly created connection with the database.
  • Go to your website repository and edit the following files:
  • Gemfile:
# ...
group :jekyll_plugins do
  # ...
  # github.com/seroperson/jekyll-fetch-notion
  gem 'jekyll-fetch-notion'
  • _config.yml:
# _config.yml
# ...
# set default layout for your posts
# make sure it set because otherwise your notion-powered
# posts will look weird
  - scope:
      path: ""
      type: posts
      layout: post
      sitemap: true
      hidden: false
# ...
  # disables the sync at build stage
  # makes `jekyll prefetch` command available
  fetch_mode: true
    # your database id
    # https://www.notion.so/{workspace_name}/{database_id}?v={view_id}
    - id: 123abc
      # filter based on your database properties'
      # replace with your own
      filter: { "property": "Status", "select": { "equals": "published" } }
  • (optionally) Configure an additional CI job in .gitlab-ci.yml (or follow the guide of how to do it according to CI you use), so you can trigger synchronization just by pressing a button:
# variables must be defined
# ...
  stage: deploy
  # ...

  stage: deploy
    # run this job just only if it was scheduled or triggered manually
    - if: ($CI_PIPELINE_SOURCE == "manual" || $CI_PIPELINE_SOURCE == "schedule") && $NOTION_TOKEN != null && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - |-
      # run our synchronization command
      bundle exec jekyll fetch_notion
      # if there are any changes from notion ...
      # note: be sure that all files produced by 
      #       (i.e. bundle install) are git-ignored
      if [[ $(git status --porcelain) ]]; then
        git config user.name $BOT_NAME
        git config user.email $BOT_EMAIL

        git add .
        git commit -m "Notion sync $(date +"%Y-%m-%d")"

        git remote add ci "https://$TOKEN_NAME:$ACCESS_TOKEN@gitlab.com/<username>/<your-repo>.git"
        git push ci HEAD:$CI_COMMIT_REF_NAME
  • (optionally) Schedule a build via GitLab UI

Make sure all necessary environment variables are set.

That’s it! Now your Jekyll website is Notion-synchronized. This post is the first one written with such synchronization. 🔔 Stay tuned for updates (🛫 Telegram channel / 📶 RSS / ✉️ Email). Have a nice day!