Build a blog with Nix
Vision
I have been learning nix for a while. Overall, I am really happy with Nix ecosystem. With Nix, home-manager, cachix, you have setup a reliable and efficient development environment and CI. For demonstration, I want to show you how I use nix to publish my blog:
- compile a Hakyll program
- generate the static site using the Hayll program
- Upload the site to Firebase
If you are new to Nix, I hope you can find something useful in this blog.
Why Nix ecosystem
Hopefully, at this point you are already sold on nix. If not, maybe checkout build with nix or nix.dev. For me, the biggest selling point of is: you can use a declarative language to set up a reproducible environment for your local development and CI.
Why Hakyll
This is probably a tough sell if you don’t care about Haskell. Nowadays, you use tools like Hugo, WordPress, or Jekyll to effortless setup a static site. I love Haskell, and Hakyll gives me an opportunity to use Haskell.
Why Firebase
There are lots of decent alternative to Firebase for hosting a static site. Just to list few
- GitHub page
- aws s3
- netlify seems really nice. probably worth trying out
- Google cloud storage can host a static site, but currently it does not support SSL.
Required Tools
- nix (either single user or multi-user installation would work)
- direnv or nix-direnv recommend using home-manager to get it)
- lorri (optional, only if direnv by itself doesn’t work well enough for you)
- cachix (optional)
Consider using nix project template
Both templates are very opinionated. getting-started-nix-template is a more generic nix template. hakyll-nix-template is Hakyll specified, it has sitemap, tag support.
scaffold hakyll project using hakyll-init
Hakyll comes with a command line too haskyll-init
to scaffold a Hakyll
project. Since we only need haskell-init
once, normal nix workflow
would be
but depend on which nix channel is your default channel, you might get
an error about pkgs.haskellPackages.hakyll
is broken. We could use
-I
flag to pin our one-time shell package to stable version. The
pattern is
-I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/{rev}.tar.gz~
The project is just a plain cabal project. We can edit project or executable name. We can use normal Nix Haskell development workflow.
Nix Haskell Workflow
Currently, there are two major Nix Haskell workflows: iohk-haskell.nix and your traditional nixpgs. People usually recommend iohk one for complex project, I haven’t used it at all.
https://discourse.nixos.org/t/nix-haskell-development-2020/6170
hoogle server --local -p 3000 -n
How to find certain (Haskell) package’s version
How to customize Hakyll
This is probably beyond the scope of this blog, Robert Pearce has an on-going serial on the topic. https://robertwpearce.com/hakyll-pt-1-setup-and-initial-customization.html
Here is a list of Hakyll projects I often check
- https://github.com/abhin4v/abhin4v.github.io/tree/source
- https://github.com/sdiehl/stephendiehl.com
- https://github.com/kowainik/kowainik.github.io/blob/develop/src/Kowainik.hs
- https://github.com/ysndr/blog
- https://github.com/patrickt/patrickt.github.io
- https://github.com/rpearce/robertwpearce.com
Hakyll website has a more comphersive list
GitHub Action
Build Step
Most of the YAML configuration is copied from
getting-started-nix-template. My default.nix
only build the Hakyll
program, it doesn’t generate the site. So I added
result/bin/site build
in run command. (site
is the name of my Hakyll
executable). We need pass generated site directory as
artifacts
between build steps
dist
is the directory name for the generated site, by default Hakyll
uses _site
.
Publish to Firebase
I use w9jds firebase action to publish the generated static site directory to Firebase. There are publish actions for netlify and Github Page. Of course, we have to store our Firebase token as encrypted secret and pass them as environment variables into the build step.
- https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets
- https://github.com/w9jds/firebase-action#environment-variables
Enable cachix cache (Optional)
- https://nix.dev/tutorials/continuous-integration-github-actions.html
- https://github.com/cachix/cachix-action
Current version of GitHub Action YAML
Result
Right now, the whole CI steps averagely takes 4 min to run. I am pretty happy with the setup.
References
About Nix in general
Nix Haskell development
- https://github.com/Gabriel439/haskell-nix/
- https://discourse.nixos.org/t/nix-haskell-development-2020/6170 (probably more up to date)
Hakyll
- https://robertwpearce.com/hakyll-pt-6-pure-builds-with-nix.html
- https://jaspervdj.be/hakyll/tutorials/github-pages-tutorial.html