Building a Blog with Emacs Org-Mode

This year as my WordPress account came near expiring I thought I would finally take a moment to rebuild my website using purely Emacs and org-mode. WordPress, while a fine CMS is overkill for my simple needs. I have no need for a built-in editor, themes, or plug-ins I just need simple HTML page generation.

While using WordPress I wrote my posts in org-mode and I imported them to WordPress with the org2blog package. This was an okay solution, but I was never truly happy with how clunky of an experience it was. Cheap WordPress hosting plans offer few customization options and WordPress felt like overkill when I knew that org-mode had built in HTML exporting.


  1. Have a lightweight and consistent layout and structure for my site
  2. Be able to clone my blog's repo and run make to generate my site from scratch
  3. Be able to quickly sync exported files with server make install
  4. Do this without adding new dependencies (e.g. just git, emacs, make, ssh, rsync)


  My choice Other options
Linux Server Digital Ocean Physical server
Web Server Apache Nginx, lighthttpd
Syncing scp rsync, syncthing
Site Generation Emacs Hugo?, pandoc?


To do this I took advantage of the built in export features inside org-mode which allows us to define how to export the org files, where to export them to, as well as add HTML preamble and postamble for navigation. We will also use this functionality to insert a CSS style and copy files like images.

;;; publish.el --- generate and publish my blog

;; Copyright (C) 2018 Thomas Ingram

;;; Commentary:


;;; Code:
(require 'ox-publish)

;; Temperarily disable htmlize
(setq org-html-htmlize-output-type `nil)

(defun org-sitemap-custom-entry-format (entry style project)
  (let ((filename (org-publish-find-title entry project)))
    (if (= (length filename) 0)
	(format "*%s*" entry)
      (format "%s [[file:blog/%s][%s]]"
	      (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))

(setq org-publish-project-alist
	:base-directory "~/Documents/blog/org/"
	:base-extension "org"
	:publishing-directory "~/Documents/blog/html/"
	:publishing-function org-html-publish-to-html
	:recursive nil
	;; :auto-sitemap t
	;; :sitemap-filename ""
	;; :sitemap-title "Thomas Ingram's Personal Website"
	:section-numbers nil
	:with-toc nil
	:html-head "<link rel=\"stylesheet\"
		  href=\"style.css\" type=\"text/css\"/>"
	:html-preamble "
<nav class=\"navigation\">
  <li><a href=\"/index.html\"><img src=\"images/home.png\"></a></li>
  <li align=right>Last Updated %C</li>
	:html-postamble "
<a rel=\"license\" href=\"\">
  <img alt=\"Creative Commons License\" style=\"border-width:0\" src=\"\" />
<br />
This work is licensed under
<a rel=\"license\" href=\"\">
  Creative Commons Attribution-ShareAlike 4.0 International License
	 :base-directory "~/Documents/blog/org/blog/"
	 :base-extension "org"
	 :publishing-directory "~/Documents/blog/html/blog/"
	 :publishing-function org-html-publish-to-html
	 :section-numbers nil
	 :with-toc nil
	 :htmlized-source nil
	 :auto-sitemap t
	 :sitemap-filename ""
	 :sitemap-sort-files anti-chronologically
	 :sitemap-format-entry org-sitemap-custom-entry-format
	 :html-preamble "
<nav class=\"navigation\">
  <li><a href=\"/index.html\"><img src=\"../images/home.png\"></a></li>
  <li><a href=\"/blog/\"><img src=\"../images/folder.png\">Blog</a></li>
  <li><img src=\"../images/file.png\">%t.txt</li>
	 :html-head "<link rel=\"stylesheet\"
		  href=\"../style.css\" type=\"text/css\"/>")
	 :base-directory "~/Documents/blog/org/"
	 :base-extension "css\\|pdf"
	 :recursive t
	 :publishing-directory "~/Documents/blog/html/"
	 :publishing-function org-publish-attachment)
	 :base-directory "~/Documents/blog/org/images/"
	 :base-extension "jpg\\|gif\\|png"
	 :publishing-directory "~/Documents/blog/html/images/"
	 :publishing-function org-publish-attachment)
	("" :components ("home" "blog" "static" "images"))))

(org-publish-all t)

;;; publish.el ends here

In this config I do not make use of Emacs' tramp features which would allow for directly exporting these files to the remote server by changing :publishing-directory to something like "//". In my (limited) testing I have found this to be far to slow in comparison with syncing the files after export.


	emacs -Q --batch -l publish.el -f org-publish-all

publish: all
	rsync -e ssh -vr html/

Author: Thomas Ingram

Created: 2019-05-09 Thu 19:19