Skimpy is a simple file based CMS that can be used to make a website or blog. Skimpy is built for developers, though anyone who can create files and put a PHP site on the internet can use it. Skimpy aims to be simple and easy to use.

See the final documentation section for details on how Skimpy actually works.

Quick Video Overview


Download Latest

Server Requirements

  • PHP >= 7.2
  • OpenSSL PHP Extension
  • PDO PHP Extension
  • Mbstring PHP Extension

Installing Skimpy

  1. composer create-project --prefer-dist skimpy/cms skimpy
  2. Unzip the installation or use composer (see below)
  3. cd path/to/skimpy/cms
  4. cp .env.example .env
  5. Update the .env file to match your preferences/info
  6. php -S localhost:4000 -t public
  7. Visit http://localhost:4000
You can also install with Composer
composer create-project --prefer-dist skimpy/cms skimpy

Creating a Blog Post

  1. Create a new markdown file called inside the site/content directory.
  2. Visit http://localhost:4000/test-post and voila!
  3. Add a markdown header to the file # Test Header and refresh. You'll see an h1 tag that reads "Test Header".

How URIs are determined

In the example above where we created "test-post", you can see that Skimpy uses the filename as the URI to the post. If you put the file inside a directory "products" and name the file "". The URI to the file will be /products/widget

path = path/to/skimpy/site/content/products/
URL = http://localhost:4000/products/widget

Adding Front Matter

You can customize certain properties of an entry using front matter key values. All front matter is optional and Skimpy will use sensible defaults.

Always use exactly three hyphens "---" to separate front matter from content. Otherwise, you'll get an exception or your file won't parse correctly.

Keep in mind that using three hypens "---" on their own line, or at the end of the file without a new line, will cause Skimpy to assume it is the front matter separator whether that is what you intend or not.

Native Front Matter Keys
Key Description
title Custom title if different from the filename
date The published date of the content. Always use PHP Y-m-d or Y-m-d H:i:s format. DATES MUST BE QUOTED "2020-01-22"
seoTitle The content to put in the head <title> tag
description The SEO meta description for this page (if any)
template The name of the template to use if not using whichever template matches the convention for this content type
categories The categories the content belongs to
tags The tags to assign to the content

Front Matter Metadata

Any key/value you put in front matter that isn't built in will be stored as metadata.

You can access any custom front matter key values in your templates.

The "restaurants" key in the example file below is an example of metadata.

Accessing FrontMatter Metadata in Templates

Call the "meta" method on the entry object and pass in the key you want.

{{ entry.meta('restaurants') }}

Example Content File With Front Matter

title: My 2019 Trip to Germany
date: "2019-08-27"
description: The SEO meta description (if any) goes here
seoTitle: Custom SEO title here
categories: [Vacation]
tags: [Personal Growth]
restaurants: [Kin Dee, Markthalle Neun]
The content for the post goes here...

Creating a Page

Skimpy sites don't have to be blogs at all but it does make blogging easy if that's what you are doing. Before I explain "pages" in Skimpy, let's first define what a page is. Both pages and posts are just files that hold content for display when someone hits the matching URI. The only difference between a "page" and a "post" is that people don't typically display an index of pages like they do blog posts. Pages are not usually listed on the home screen with "excerpts" like blog posts and pages aren't really meant to be categorized or tagged.

So if you would like to create an "entry" on your site that doesn't show up in your default blog feed, then you want to create a page. This is accomplished by creating a subdirectory under the content directory and placing any files that you want to be a "page" inside that directory. You can call the directory anything you want but keep in mind the directory name will be part of the URI to access the page.

You may also exclude root entries from displaying on the home page or an index by manually changing the content type via the Front Matter "type" key. This essentially is the same as placing the file in a subdirectory. Any file inside a subdirectory has a "type" property with a value matching the name of the parent directory. Try and avoid using the "type" key if you can. Sites that follow the conventions will be more well organized and easier to navigate. You can always just make a "pages" directory and put your pages there. Having a URI segment "pages" proceed the page URI really isn't a big deal.

Taxonomies - Categorizing & Tagging Content

Content can be categorized or tagged. Categories and tags are referred to as "taxonomies" (like in WordPress). Taxonomies are a way of classifying content. There are already two predefined taxonomies in the default Skimpy installation. And you guessed it, those taxonomies are categories and tags. If you wanted to add your own taxonomy, you would just copy the categories.yaml file and change the values.

Taxonomies have "terms". In the case of the "categories" taxonomy, the terms are the actual categories. You add new categories by adding a new array to the terms key in the content/taxonomies/categories.yaml file. You simply provide a name for the category and a slug to be used in the URI.

Public Terms Route

By default, Skimpy will list links to the terms (category names) you have defined in the categories.yaml file, when you visit /categories. If you want to turn this off for a particular taxonomy (like categories) then you should set the has_public_terms_route yaml key to false in categories.yaml.

Assigning Categories/Tags

You simply add the key categories to the YAML front matter of any content file and provide an array of category names. Use the actual "name" NOT the slug.

Don't forget the Front Matter separator! "---"

categories: [Web Development]
tags: [Tag 1]
date: "2020-01-21"
Your post content here...

Creating Page Types

Let's say you want to add a page for every person on your team and you want to list those people on your "team" page.

  1. Create folder content/team
  2. Create file content/team/
  3. Create file content/team/
  4. Enable a "team index" by creating content/team/
  5. The following URLs are now valid
    • - Lists links to any file under the "team" directory

Index Pages

Index pages are basically an archive of content files inside of a subdirectory of content.

See the example for creating page types

By default, there is no index for files inside a subfolder of the content directory. To turn the index uri on, in other words, to make the subfolder name a valid URI on your website, you just create a file inside the subdirectory called

URI Mapping & Template Variables

There are three types of "entities" in Skimpy - entry, taxonomy, and term. The current URI determines what type of entity is queried and what the template variable names are. You can determine what file is being displayed just by looking at the URI. The file structure maps directly to the current URI. Just keep in mind when you create a taxonomy file, you are creating a URL that matches the name of that file. When you create a directory inside content and you place an file in it, you are creating a URL that matches the path to that directory. If that isn't simple enough for you, see the table below.

URI Mapping
When URI Matches You'll See Template Variable
the name of a content file The files markdown content entry
the name of a taxonomy file (like "categories") A listing of links to the terms registered to the taxonomy taxonomy
the name of a term (like a category name) A listing of links to entries with that term assigned in their Front Matter term

Template Hierarchy

Skimpy uses Twig for templating. Please see the Twig Docs to discover all the power of Twig.

Which template is used depends on the type of entity (entry, taxonomy, term) being displayed. You can manually set the template with the template front matter key

  1. FrontMatter key template: your-template-name
  2. The parent folder name if the file is in a subdirectory of content
  3. If the file is an index (, the index template.
  4. The entity type (entry = entry.twig, taxonomy = taxonomy.twig, term = term.twig)

Custom Templates

Skimpy uses Twig for templating. Please see the Twig Docs to discover all the power of Twig.

Skimpy has some conventions for deciding what template to use. You can override the conventions by adding a template key to the front matter of a content file.

Using a custom template

  1. Create the template site/templates/my-custom-template.twig
  2. Open or create a content file and set the template in the Front Matter.
    template: my-custom-template


Deploying Skimpy to production is easy. Just make sure your server meets the server requirements and create a virtual host with the document root set to the skimpy public folder.

Auto Rebuild

What is auto rebuild? Auto rebuild refers to Skimpy scanning your content folder and updating the SQLite DB on every request to your website. This makes development quick and easy. You don't have to worry about "generating" your website. You can turn this feature on or off in your local or production environment. I wouldn't really worry about it too much because it's not going to make much of a difference. That being said I will share my preferred way of deploying with Skimpy.

How I Deploy Skimpy

  1. I track my SQLite DB with Git so it gets automatically deployed when I push to master. This allows my Skimpy sites to be updated to latest when I push to master without the production sites having to rebuild the database on each request

  2. I set AUTO_REBUILD=false in my .env file on the production site so the database won't be rebuilt on every request.

How does Skimpy work?

When a request hits your website, Skimpy scans all of the files in your site/content directory, converts them to Doctrine entities, and shoves them into an sqlite database. You don't have to pay any attention to the database at all if you don't want. The reason Skimpy converts your content into database records is so that it can take advantage of all of the power of doctrine and SQL in general. The database component in Skimpy is used more like a cache. You should never be editing your database directly as Skimpy will just wipe out any changes you made automatically the next time your website receives a request.

Skimpy uses Doctrine and a database for several reasons. The primary reason for the DB is so that you don't have to "generate" your actual website everytime you make changes to your content, or run a "watch" command that generates the changes when you save a file. Doing things the "generater way" is just nonsense. Generating adds complexity to creating a website or blog. Writing and adding content should be mindless and that's what Skimpy sets out to accomplish.