Auto-updating Hugo blog with Syncthing and systemd
You’re looking at a static site built with Hugo. It is hosted from the Raspberry Pi 3b somewhere in the back of a cupboard in my living room. To update this site, I wanted a solution that
- does not require me to SSH into my RPi
- does not require me to be connected to the Internet
- does not require me to remember to update the site
For now, I think I accomplished this thusly:
Prerequisites:
I gently assume that
- you have Syncthing working on the Raspberry Pi and other computer(s)
- you have a webserver running on the RPi
Hugo installation
As Hugo is a Go program, installation was fairly simple:
- Find the
_linux-arm.tar.gz
from the latest release at https://github.com/gohugoio/hugo/releases, copy the link, and download the file to the RPi:cd ~/hugo; wget -O hugo.tgz "https://github.com/gohugoio/hugo/releases/download/v${LATEST_VERSION}/hugo_${LATEST_VERSION}_linux-arm.tar.gz"
- Untar the archive. Be sure to do it right the first time:
tar xzf hugo.tgz #eXtract Zhe File
- Link the binary to somewhere in your
$PATH
cd ~/bin; ln -s $HOME/hugo/hugo
- Verify that it works:
hugo version # should say something like hugo v$LATEST_VERSION-
Create a Hugo site
Basically: follow the Hugo Quick-start
-
Create a new site:
cd ~; hugo new site dirk.ndrvn.nl
-
Install a shiny theme:
cd ~/dirk.ndrvn.nl; git clone https://github.com/janraasch/hugo-bearblog.git themes/hugo-bearblog; echo "theme = 'hugo-bearblog'" >> hugo.toml
-
Create content that people want to read:
hugo new content tech/hugo-blog-syncthing-systemd.md
(Actually writing the post is left as an exercise for the reader)
-
I publish the
public/
directory with Nginx:server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name dirk.ndrvn.nl; ssl_certificate /etc/letsencrypt/live/dirk.ndrvn.nl/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/dirk.ndrvn.nl/privkey.pem; location / { root /home/pi/dirk.ndrvn.nl/public; index index.html; } }
And reload nginx to actually start serving out content:
nginx -T && nginx -s reload
Syncthing the site
Create a new Synchting share. To avoid passing around the built site, I added these things to .stignore
:
public
.hugo_build.lock
Do the auto-update
To auto-update the site, I added a systemd unit in /etc/systemd/system/hugo-watch.service
to run hugo
whenever a file in ~/dirk.ndrvn.nl
is changed :
[Unit]
Description=Hugo Watcher for dirk.ndrvn.nl - Rebuild on file change
After=network.target
[Service]
User=pi
WorkingDirectory=/home/pi/dirk.ndrvn.nl
ExecStart=/bin/bash -c "inotifywait -m -e modify,create,delete,move -r --exclude '(^/home/pi/dirk.ndrvn.nl/(public/|build.log|\..*)|\.syncthing\.)' /home/pi/dirk.ndrvn.nl | while read -r FILE; do echo "$FILE"; /home/pi/bin/hugo --panicOnWarning --logLevel debug --verbose | tee /home/pi/dirk.ndrvn.nl/build.log; done"
Restart=always
[Install]
WantedBy=multi-user.target
This does the following things:
- as user
pi
, in the directory/home/pi/dirk.ndrvn.nl
- watch for changes in
/home/pi/dirk.ndrvn.nl
and all subdirectories, excludingpublic/
,build.log
, dotfiles, and.syncthing.*
, withinotifywait
- pipe those events (which are emitted line-based) to a
while read
loop - for each change, run
/home/pi/bin/hugo --panicOnWarning --logLevel debug --verbose | tee /home/pi/dirk.ndrvn.nl/build.log
This of course requires inotifywait
to be installed:
sudo apt install inotify-tools
After this, I was able to start the hugo-watch
service, and enable it to start at boot:
sudo systemctl start hugo-watch.service;
sudo systemctl enable hugo-watch.service;
Conclusion
With this setup, I’ll be able to edit this website from all computers I have shared the Syncthing folder.
After saving hugo-blog-synchting-systemd.md
on a computer, it is synchronised to the Raspberry Pi, and a new version of the site is automatically updated.
To verify everything is working, you can tail the systemd journal:
sudo journalctl -f -u hugo-watch
Which yields output similar to this:
Sep 06 14:15:27 pi bash[25826]: /home/pi/dirk.ndrvn.nl/content/tech/ MOVED_FROM hugo-blog-syncthing-systemd.md
Sep 06 14:15:29 pi bash[7808]: Start building sites …
Sep 06 14:15:29 pi bash[7808]: hugo VendorInfo=gohugoio
Sep 06 14:15:29 pi bash[7808]: INFO static: syncing static files to / duration 7.095755ms
Sep 06 14:15:29 pi bash[7808]: INFO build: step process substep collect files 2 files_total 2 pages_total 2 resources_total 0 duration 24.874519ms
Sep 06 14:15:29 pi bash[7808]: INFO build: step process duration 32.150691ms
Sep 06 14:15:29 pi bash[7808]: INFO build: step assemble duration 9.580869ms
Sep 06 14:15:29 pi bash[7808]: INFO build: step render substep pages site en outputFormat html duration 125.902392ms
Sep 06 14:15:29 pi bash[7808]: INFO build: step render substep pages site en outputFormat rss duration 10.562434ms
Sep 06 14:15:29 pi bash[7808]: INFO build: step render pages 12 content 6 duration 137.051391ms
Sep 06 14:15:29 pi bash[7808]: INFO build: step render deferred count 0 duration 8.75µs
Sep 06 14:15:29 pi bash[7808]: INFO build: step postProcess duration 116.303µs
Sep 06 14:15:29 pi bash[7808]: INFO build: duration 183.931825ms
Sep 06 14:15:29 pi bash[7808]: | EN
Sep 06 14:15:29 pi bash[7808]: -------------------+-----
Sep 06 14:15:29 pi bash[7808]: Pages | 12
Sep 06 14:15:29 pi bash[7808]: Paginator pages | 0
Sep 06 14:15:29 pi bash[7808]: Non-page files | 0
Sep 06 14:15:29 pi bash[7808]: Static files | 0
Sep 06 14:15:29 pi bash[7808]: Processed images | 0
Sep 06 14:15:29 pi bash[7808]: Aliases | 0
Sep 06 14:15:29 pi bash[7808]: Cleaned | 0
Sep 06 14:15:29 pi bash[7808]: Total in 374 ms
Sep 06 14:15:29 pi bash[25826]: /home/pi/dirk.ndrvn.nl/content/tech/ MOVED_TO hugo-blog-syncthing-systemd.md
Sep 06 14:15:29 pi bash[7819]: Start building sites …
Sep 06 14:15:29 pi bash[7819]: hugo VendorInfo=gohugoio
Sep 06 14:15:29 pi bash[7819]: INFO static: syncing static files to / duration 937.712µs
Sep 06 14:15:29 pi bash[7819]: INFO build: step process substep collect files 2 files_total 2 pages_total 2 resources_total 0 duration 2.636156ms
Sep 06 14:15:29 pi bash[7819]: INFO build: step process duration 10.975353ms
Sep 06 14:15:29 pi bash[7819]: INFO build: step assemble duration 4.143088ms
Sep 06 14:15:29 pi bash[7819]: INFO build: step render substep pages site en outputFormat html duration 138.364677ms
Sep 06 14:15:29 pi bash[7819]: INFO build: step render substep pages site en outputFormat rss duration 9.479462ms
Sep 06 14:15:29 pi bash[7819]: INFO build: step render pages 12 content 6 duration 150.339878ms
Sep 06 14:15:29 pi bash[7819]: INFO build: step render deferred count 0 duration 11.563µs
Sep 06 14:15:29 pi bash[7819]: INFO build: step postProcess duration 65.052µs
Sep 06 14:15:29 pi bash[7819]: INFO build: duration 167.015356ms
Sep 06 14:15:29 pi bash[7819]: | EN
Sep 06 14:15:29 pi bash[7819]: -------------------+-----
Sep 06 14:15:29 pi bash[7819]: Pages | 12
Sep 06 14:15:29 pi bash[7819]: Paginator pages | 0
Sep 06 14:15:29 pi bash[7819]: Non-page files | 0
Sep 06 14:15:29 pi bash[7819]: Static files | 0
Sep 06 14:15:29 pi bash[7819]: Processed images | 0
Sep 06 14:15:29 pi bash[7819]: Aliases | 0
Sep 06 14:15:29 pi bash[7819]: Cleaned | 0
Sep 06 14:15:29 pi bash[7819]: Total in 247 ms