Open-source project
InkProject/ink avatar
InkProject/ink

Ink: a Go static blog generator with Go template themes

An elegant static blog generator

1,089 stars117 forksGoLicense varies

At a glance

What is it?
Ink, called InkPaper in its documentation, is a static blog generator written in Go with YAML configuration, Markdown articles, Go template themes and a live preview server. It is small enough to read in an afternoon, and its licence is the one detail that needs checking before you build a business site on it.
Who is it for?
Adopt Ink if you want a static blog you can read in full, with a live preview, a plain YAML configuration and a theme you can edit as Go templates. Do not adopt it for a commercial site until you have checked the licence, because the project ships under CC Attribution-NonCommercial 4.0, and do not expect the dependency set to be current, since the module pins a release candidate of a YAML library.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 67 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Small enough to read, and named two ways

Ink is a static blog generator developed in Go, with the stated qualities of no dependencies, cross platform, easy to use, fast building times and an elegant theme. The README calls the project InkPaper throughout, while the repository, the module path and the binary are all ink, which is a small inconsistency that will cost you a search result or two.

The claim of no dependencies is about the end user rather than about the developer. The go.mod file lists seven direct dependencies, which is a modest set by the standards of a Go web project: fsnotify for file watching, gomarkdown/markdown for the Markdown parsing, gorilla/feeds for RSS, gorilla/websocket for the live preview connection, snabb/sitemap for sitemap generation, urfave/cli v3 for the command line, and go.yaml.in/yaml/v4 for YAML parsing. The feature list is worth reading as a description of what a blog generator is expected to do in 2026. YAML configuration, Markdown articles, fast builds, multiple article authors, archive and tag generation, real-time preview when saving, offline full-text keyword search, and LaTeX style math formula support through MathJax.

Two of those are unusual. Offline full-text search means the search index is built at compile time and shipped with the site, so a reader searching your archive gets results without a server. And MathJax support means the maths is rendered in the browser, which is the standard approach and the one that keeps the generator simple.

The licence is the Creative Commons Attribution-NonCommercial 4.0, which is a theme licence rather than a software licence, and it has a practical consequence that the section near the end returns to.

One YAML file describes the site

The configuration lives in config.yml at the root of the blog directory, and it is a small, flat document.

yaml
site:
    title: Website Title
    subtitle: Website Subtitle
    limit: Max Article Count Per Page
    theme: Website Theme Directory
    comment: Comment Plugin Variable (Default is disqus username)
    root: Website Root Path # Optional
    lang: Website Language # Support en, zh, ru, ja, de, pt-br, configurable in theme/lang.yml
    url: Website URL # For RSS generating
    link: Article Link Scheme # Default is {title}.html, Support {year}, {month}, {day}, {hour}, {minute}, {second}, {title} variables

authors:
    AuthorID: # Your author ID, used in article's author field
        name: Author Name
        intro: Author Motto
        avatar: Author Avatar Path

build:
    output: Build Output Directory # Optional, default is "public"
    port: Preview Port
    copy:
        - Copied Files When Build
    publish: |
        Excuted command when 'ink publish' is used

Several of these fields carry more design than they first appear. The link field defines the article URL scheme, defaulting to {title}.html and supporting the {year}, {month}, {day}, {hour}, {minute}, {second} and {title} variables, so a date-based or title-based permalink is a configuration change rather than a template change.

The lang field selects a language, with en, zh, ru, ja, de and pt-br named, and the actual translations live in theme/lang.yml, so localisation is a theme concern rather than a generator one. The build.port field is the preview port, and the build.publish field holds a shell command that runs when you publish, which is where you hook in your rsync or your deploy script.

The authors block is a map keyed by an author ID, and that ID is what an article's author field refers to, so multiple authors are a first-class concept rather than a theme hack.

Article front matter is YAML above a Markdown body

Articles are Markdown files in the source directory, and subdirectories are supported, so you can organise by year or by category. The metadata sits in a YAML block at the top of the file, separated from the body by a line of dashes.

yaml
title: Article Title
date: Year-Month-Day Hour:Minute:Second #Created Time. Support timezone, such as " +0800"
update: Year-Month-Day Hour:Minute:Second #Updated Time, optional. Support timezone, such as " +0800"
author: AuthorID
cover: Article Cover Path # Optional
draft: false # Is draft or not, optional
top: false # Place article to top or not, optional
preview: Article Preview, Also use <!--more--> to split in body # Optional
tags: # Optional
    - Tag1
    - Tag2
type: post # Specify type is post or page, optional
hide: false # Hide article or not. Hidden atricles still can be accessed via URL, optional
toc: false # Show table of contents or not, optional
---

Markdown Format's Body

The fields are all optional apart from the obvious ones, and each of them maps to a template variable. The preview field and the more comment are the two ways to control the teaser text, so you can either write a summary in the front matter or split the body at a marker. The toc flag asks for a table of contents, which is generated from the headings in the body.

Two fields deserve a warning. The hide flag removes an article from the listings, and the documentation is explicit that hidden articles can still be accessed via URL, so it is a listing switch and not a secret. And the draft flag is how you keep something out of the published site, which is the difference between the two.

The type field distinguishes a post from a page, and pages are what a static site usually uses for the about and contact pages that do not belong in the archive.

Themes are Go templates with a JavaScript build step

The default theme lives in the theme folder, and it is the part of the project that most users will change. The theme uses page.html for the article list and article.html for the individual article, and the variables are referenced with Golang Template syntax.

The theme is built with npm, so run npm install and npm run build in the theme folder to rebuild it. That is a JavaScript toolchain inside a Go project, and the reason is that the theme is a static site in its own right with assets that need bundling. The Go side only has to copy the result.

Any .html file you create is copied to the source directory, and it can use all the variables from the site field in config.yml. The page templates also get custom variables, which must be placed under site.config:

yaml
site:
    config:
        MyVar: "Hello World"

and referenced in the page as {{.Site.Config.MyVar}}. The case sensitivity rule is called out specifically. Although the field names in the rest of config.yaml are all lowercase, the name of a custom variable has to be used exactly, so a variable named MYVAR_aAa must be referenced as {{.Site.Config.MYVAR_aAa}}.

There is also a small set of template functions, marked experimental, which work in HTML pages but not in .md source files. One example is readFile "path/to/file", which reads a file and includes its content in the page without any processing. The path rule is contextual: in the source directory it is relative to source, and in other directories it is relative to the theme. The full list is in funcs.go.

The alternative to writing templates is to pick one of the existing themes, and the README lists four, including the official dark theme, plus simple, story and material2 themes maintained by other people.

Preview, build and publish

The workflow is three commands, and the preview server is the one you will spend your time in.

You download and extract the release build of ink and run ink preview, with the note that on Linux and macOS you use ./ink preview. Then you open http://localhost:8000 in your browser. The preview watches the source directory, and when files change it automatically rebuilds the blog, so a refresh in the browser shows the change. The file watching is fsnotify and the live update channel is a websocket, which is how the browser is told to reload rather than the page simply going stale.

For publishing there are two paths. Run ink publish in the blog directory to build and publish automatically, and the command that gets executed is the build.publish field in config.yml. Or run ink build to produce the generated public directory and deploy it yourself, with the output directory configurable through build.output and defaulting to public.

That split is the right one for a static generator. The publish hook means the same command works locally and on a build server, and the manual build means you are never forced to hand your deploy credentials to the generator.

The build output is a directory of static files, and the generator also produces the RSS feed through gorilla/feeds, using the site url for the absolute links, and a sitemap through snabb/sitemap.

Migrating from Jekyll or Hexo, and building from source

There is a migration path, marked beta, that handles simple Jekyll and Hexo post conversions. The usage is one command:

shell
ink convert /path/_posts

The path is the conventional Jekyll posts directory, so the tool is aimed at people moving an existing blog rather than at people starting fresh. Beta is the honest word for it, so the sensible approach is to run it on a copy and check the result, since front matter conventions differ between generators.

Building from source takes three steps. Install a Golang environment, then run git clone https://github.com/InkProject/ink && cd ink && go install to compile and install the binary, and finally run ink preview $GOPATH/src/github.com/InkProject/ink/template to preview the bundled blog template. The repository root holds api.go, build.go, funcs.go, main.go, parse.go, render.go, serve.go and util.go, which is the whole program in eight files, plus a template/ directory with the default theme and a release.sh script.

There is a Dockerfile too, and it is worth noting how old it is. It is based on golang:1.18, copies the source to /code, runs go install, exposes port 8000 and runs ink preview template as the command. The documented usage is docker build -t ink . in the source directory and docker run -p 8888:80 ink.

That base image is a real caveat. The go.mod requires Go 1.25.0, so the Dockerfile's Go 1.18 builder is several major versions behind what the module asks for, and the container build is the part of the project most likely to need adjusting before it works on a current machine.

The licence is the constraint, and the release cadence is slow

The licensing deserves its own paragraph, because it is unusual for a tool and it has a direct effect on what you can do with the result.

The project is released under the Creative Commons Attribution-NonCommercial License 4.0, and the badge at the top of the README says the same. The templates and the default theme are what that licence is aimed at, and the practical question is whether the theme you end up shipping is a derivative of the project's own theme. If you build a commercial site on the default theme, the non-commercial term is the part you need to think about, and it is worth reading the licence text rather than assuming the terms of the software underneath.

The maintenance picture is quiet. The repository is not archived, the last push was on 2026-08-17, and the release list contains a single entry, nightly, dated 2023-02-08. So the project is maintained in the sense that commits land, but it does not cut versioned releases in the way an actively released tool would, and the dependency pins in go.mod tell the same story: a YAML library at a 4.0.0 release candidate and a Markdown library pinned to a 2026 pseudo-version.

Those pins are the other practical caveat. A release candidate of the YAML library means the configuration parsing sits on a dependency that has not had a stable release, which is fine for a project of this size and would be a concern in a larger one.

The README also lists a set of blogs driven by InkPaper, which is a reasonable way to see what the generator looks like in the wild, and it links a tutorial on deploying an Ink blog to GitHub Pages with Travis CI, which is dated but still shows the shape of the deployment.

Editorial conclusion

Adopt Ink if you want a static blog you can read in full, with a live preview, a plain YAML configuration and a theme you can edit as Go templates. Do not adopt it for a commercial site until you have checked the licence, because the project ships under CC Attribution-NonCommercial 4.0, and do not expect the dependency set to be current, since the module pins a release candidate of a YAML library. Before you start, read the theme's page.html and article.html to see the template variables, and if you are migrating an existing Jekyll or Hexo blog, try ink convert on a copy of your posts directory first.

Frequently asked questions

How do I start a blog with Ink?

Download and extract a release of ink, run ink preview, and open http://localhost:8000 in your browser. The preview rebuilds automatically when files change, and you publish with ink publish or build with ink build to produce the public directory yourself.

What licence is Ink released under?

The Creative Commons Attribution-NonCommercial License 4.0, which applies to the project and its themes rather than being a conventional software licence. The repository metadata does not list a licence separately, and the non-commercial term is the part to consider before using the default theme for a commercial site.

How do I customise the theme in Ink?

The default theme is in the theme folder and is rebuilt with npm install and npm run build. Templates use Golang Template syntax, with page.html for the article list and article.html for an article, and custom variables are defined under site.config in config.yml and referenced as {{.Site.Config.MyVar}} with exact case.

Can Ink import an existing Jekyll or Hexo blog?

There is a beta migration feature that performs simple Jekyll and Hexo post conversions, run as ink convert /path/_posts. Because it is labelled beta, run it against a copy of your posts and check the converted front matter.

What are the front matter options for an Ink article?

A YAML block with title, date and optional update, both accepting timezone offsets such as +0800, plus author, cover, draft, top, preview, tags, type to distinguish a post from a page, hide to remove an article from listings while keeping it reachable by URL, and toc for a table of contents.

Official sources

  1. InkProject/ink on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/inkproject-ink.svg)](https://hysenlabs.com/projects/inkproject-ink)

Was this useful?