Building a Static CMS

by on

We are static evangelists here at Carrot. We've written blog posts about it, participated in static hackathons, and built a significant arsenal of open source tools around static.

As a result, we've replaced a lot of our typical database driven CMS projects with static builds. These projects were primarily content based and only required infrequent updates to add or remove content. It didn't make sense to build out so much infrastructure (database, hosting, caching, monitoring, code logic, admin interface, etc) for sites that are mostly responding to requests with the same HTML over and over.

To support this change, we invested heavily in building out an ecosystem of tools around our in-house static site generator: Roots. The latest version, v3, is close to a final public release, and the most exciting new feature has been the addition of a powerful extensions API. With extensions, we're able to jump in at multiple points in the compile process to add functionality and push the limit of what's possible with static. We've been making them like crazy!

In order to manage content in our new static builds, we used dynamic content, an extension that allowed us to create content in separate, structured content files. It allowed us to write out our content in Markdown separated out from our view templates, and then expose that data in the views of our Roots projects.

The move to static worked out very well from a development perspective. It simplified our development and sysops work, lowered our hosting costs, and ensured a high level of performance, scalability, and reliability.

The Challenge

However, even with dynamic content in place, every update still required a developer to update the repo and deploy the site. Even though this only took a minute, it created a bottleneck for our content managers and added up to a significant amount of developer time.

In order to make this idea work at a large scale at our company and on bigger projects, we needed to figure out how to automate the management, build, and deployment of static sites.

We made a couple attempts to build a tool ourselves, however we quickly realized that building a fully functioning CMS is a lot of work for an agency with many competing demands for our dev team. We didn't want to reinvent the wheel, so instead we started to research how we could piece together third party tools into a workable solution.

Finding the Tools

We figured that there were two large pieces that we could outsource to third party tools:

  • API Based CMS A SaaS CMS that would allow us to flexibly define data structures, manage our site assets, and expose our content data through a robust and well-documented API. It would also need webhooks to notify other services when content has changed.

  • Deployment & Hosting Platform A platform capable of listening for webhook notifications, starting builds, and deploying sites to production. It should play nice with Roots as well as other services and tech stacks, and support a powerful API to let us programmatically manage all our sites with ease.

We ended up finding two great services, Contentful and Netlify, that worked well with our current toolset to achieve the Holy Grail: a static CMS.


Contentful is a CMS that lets us define structured data models (called content types) that support most the data types you'll ever need. Editors enter content as entries that conform to one of the content types you've defined. Content can be written in Markdown, and you can even manage and insert image assets.

In stark contrast to a CMS like Wordpress, now our content data is no longer coupled to a specific templating engine or application. All content is created and edited inside Contentful, and as a result it's completely decoupled from our presentation layer. Since our content is written in semantic Markdown and exposed through a well-supported JSON API, our data is very portable and we're able to consume it using any tech stack that can understand JSON and Markdown. Furthermore, Contentful can communicate with other services via webhooks when content is changed.


Netlify is an outstanding static hosting platform that provides several useful features that bring the rest of our tools together. It supports Node.js tools like Roots, so when we deploy it can resolve all our npm dependencies, install roots, and run our roots compile build command without a hitch. Once the build is complete, we specify a folder to deploy (public in the case of Roots) and Netlify optimizes all our assets and pushes it out to a global CDN.

As an added bonus, Netlify provides integration with Github so deploys can be triggered by pushes to any specified branch. Deploys can be triggered manually through their API, but what makes it even more powerful is that it can kick off new builds when it receives a webhook request from another service through webhook endpoints you can create.

Putting the Pieces Together

We decided to use our own website as a testing ground for using these tools. We migrated our blog and press content from dynamic content into Contentful. In order to load our data that was now in Contentful back into our Roots project, we built an extension: roots-contentful. Every time our website was compiled, roots-contentful would fetch data through Contentful's API and pass it into our view templates. It would also compile each individual blog post's HTML file using the single page views feature.

With the Roots project now built on top of data from Contentful, we then set about automating deployment when content changed. First, on Netlify, we created a webhook endpoint that would start a new build and deploy the project whenever it received a POST request. Then, we set up a webhook in Contentful with the new Netlify endpoint as the destination URL. With this in place, every time an editor creates, deletes, or edits a new piece of content, Netlify receives a webhook from Contentful and kicks off a new deploy. Netlify runs roots compile, loads the new Contentful data with roots-contentful, optimizes our assets, and finally pushes it out to a global CDN. Within a couple minutes from publishing, our new content is live on our static site.

The Result

Static CMS Diagram

Now we have a full fledged CMS and we didn't even have to build most of it. We spend less time setting up, monitoring, and managing our infrastructure, and have more time to focus on building kick ass websites. Hosting costs are pennies on the dollar compared to a dynamic site and our deployment process is now automated for developers and non-developers alike.

We hope you're inspired to re-examine some of the assumptions you may have about the limits of static websites. Don't take our word for it, give it a try for your next project. We've even created a sprout template to get you set up!