-
Read the tutorial to get started! https://robertwpearce.com/the-hakyll-nix-template-tutorial.html
-
All build inputs (GHC, Hakyll, pandoc, ...) come prebuilt from the default NixOS binary cache, so a fresh clone builds without compiling the toolchain from source and needs no extra substituter setup.
-
If you make changes to anything inside of
ssg/, you'll need to clean the hakyll cache and rebuild. This is the preferred series of commands for rebuilding (with logs), cleaning the cache, and re-running the dev server:nix build --print-build-logs && \ nix run . clean && \ nix run . watch
-
Build your site into the
./result/distfolder:λ nix build -
Start hakyll's dev server that reloads when changes are made:
λ nix run . watch Listening on http://127.0.0.1:8000 ...more logs -
Run any hakyll command through
nix run .!λ nix run . clean Removing dist... Removing ssg/_cache... Removing ssg/_tmp... -
Start a development environment that
- has your shell environment
- has
hakyll-site(for building/watching/cleaning hakyll projects) - has
hakyll-init(for generating new projects) - can have anything else you put in the
shell.buildInputsof thehakyllProjectinflake.nix - is set up to run
ghciwith some defaults and the modules loaded so you can make your own changes and test them out in the ghci REPL
λ nix develop [hakyll-nix]λ hakyll-site build ... Success [hakyll-nix]λ ghci ... [1 of 1] Compiling Main ( ssg/src/Main.hs, interpreted ) ... λ >
All of this is customizable, and here are some things that are already done for you:
- pandoc markdown customization to make it as close to GitHub's markdown style as possible
sluggermodule is included that makes nice link URIs based on post titles- RSS & Atom XML feed generation
- Sitemap generation
- Code syntax highlighting customization
- HTML output minification
- Content-hashed asset cache-busting (assets get a
?v=<hash>query string so browsers refetch only when a file's contents change) - ...other reasonable defaults
Configure the dev server, cache & tmp directories, and more in
./ssg/src/Hakyll/Site/Configuration.hs.
Deployment runs through a GitHub
Action: every push to main builds the
site with Nix and publishes it to GitHub Pages
using GitHub's official Pages deployment (actions/deploy-pages). It publishes
straight to the Pages CDN — there is no gh-pages branch — and authenticates
with a short-lived
OIDC
token rather than a long-lived credential.
One-time setup for your fork:
- Go to Settings → Pages → Build and deployment → Source and select GitHub Actions.
- Custom domain (optional): set it under Settings → Pages → Custom domain.
This template ships without a
CNAMEfile so it deploys cleanly by default; if you prefer the file-based approach, add aCNAMEfile undersrc/and a copy rule for it in theforM_list inssg/src/Main.hs.
Note: If your main branch isn't named main, update branches: [main] and the
if: github.ref == 'refs/heads/main' in ./.github/workflows/main.yml.
If you don't have nix, follow the nix installation instructions.
Once you have nix installed, follow the instructions here to get access to flakes: https://nixos.wiki/wiki/Flakes.
Haskell Language Server
needs to run inside this project's Nix dev shell (which provides the matching
GHC, the site's dependencies, HLS, hlint, and ormolu) to work in your
editor. Two ways to wire that up:
- VS Code — Nix Env Selector (simplest). Install the Nix Env
Selector
extension; this repo's
.vscode/settings.jsonalready points it atshell.nix(which re-exports the flake's dev shell). No extra system tooling required — this is what the bundled.vscode/recommends. - Any editor / terminal — direnv (editor-agnostic). Install
direnvandnix-direnv, then rundirenv allowin this directory. The bundled.envrc(use flake) loads the dev shell in your terminal and any editor with direnv integration (Emacsenvrc, vim, etc.).
Both reuse the single dev-shell definition in flake.nix, and HLS loads the
Haskell project from ssg/ automatically.
This repo ships .vscode/ defaults: on first open, VS Code recommends the
Haskell
and Nix Env
Selector
extensions; .vscode/settings.json points Nix Env Selector at shell.nix and
sets haskell.manageHLS to "PATH" so the Haskell extension uses HLS from the
Nix dev shell rather than downloading its own. That HLS bundles hlint, so
lint diagnostics and "Apply hint" code actions appear automatically — no
separate linter needed. Add a .hlint.yaml at the repo root to customize the
hlint rules.
The ./.github/workflows/main.yml file pushes build results to
cachix so CI (and anyone building your site) can reuse
cached artifacts instead of rebuilding from source.
- Create a cache on cachix for your project.
- Rename the cache in
./.github/workflows/main.yml. It is currently hardcoded to this template's cache (name: hakyll-nix-template, in the "Build with cachix" step); point it at the cache you just created, otherwise pushes will fail with a permissions error. - Generate a personal auth token with write access to your cache at https://app.cachix.org/personal-auth-tokens.
- Add it as a
CACHIX_AUTH_TOKENsecret athttps://github.com/<you>/<your-repo>/settings/secrets/actions(this is the secret name the workflow reads).