Hugo Docker Image
Hugo is a popular open-source static site generator. A static site generator creates flat HTML files rather than relying on dynamic content like Javascript. Static sites typically have a smaller storage requirement and are more performant than dynamic websites, though they also have a much reduced feature set. However most people would say a site that supports full dynamic content that is used just for article content (like this site) would be a waste.
There are a fair few Hugo Docker images out there, but I decided to make my own to practice Dockerfile creation and to fully understand the image that I end up running. Please see my article regarding building a Docker image for the first time as a prerequisite to this build.
TAR file build-out
In addition to the Dockerfile for the .tar file I’ll be creating I also need to include several other files:
- entrypoint.sh file contents detailed herein; script for container start
- hugo binary downloaded from https://github.com/gohugoio/hugo/releases/latest
- nginx.conf file contents detailed herein; main configuration options for Nginx webserver
- net.redbarrel.knowhow.conf specific Nginx config for the site
SSH server is also installed so that the site contents can be managed by the administrator. Tips on defining content for a blog site in Hugo is covered in another article.
Dockerfile
FROM alpine:3.17.3
ENV SSHUSER=
ENV SSHPASSWORD=
RUN apk add --update --no-cache \
git \
gcompat \
libc6-compat \
libstdc++ \
nginx \
openssh
RUN echo 'PasswordAuthentication yes' >> /etc/ssh/sshd.config
RUN ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2
RUN mkdir /etc/nginx/sites-available /etc/nginx/sites-enabled
COPY nginx.conf /etc/nginx/
COPY net.redbarrel.knowhow.conf /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/net.redbarrel.knowhow.conf /etc/nginx/sites-enabled/net.redbarrel.knowhow.conf
COPY hugo /usr/local/bin/
RUN chmod +x /usr/local/bin/hugo
COPY hugo-cron /etc/cron.d/hugo-cron
RUN chmod +x /etc/cron.d/hugo-cron
RUN crontab /etc/cron.d/hugo-cron
RUN touch /var/log/cron.log
COPY entrypoint.sh /
WORKDIR /srv
ENTRYPOINT ["/entrypoint.sh"]entrypoint.sh
#!/bin/sh
adduser $SSHUSER
echo -n "$SSHUSER:$SSHPASSWORD" | chpasswd
chown $SSHUSER:$SSHUSER /srv
ssh-keygen -A
/usr/sbin/sshd -D -e "$@" > /dev/null 2>&1 &
/usr/sbin/crond -l 2 -L /var/log/cron.log
while true; do { if [ -d /srv/knowhow ] && [ -f /srv/knowhow/config.toml ]; then /usr/local/bin/hugo server -D -s /srv/knowhow --bind=0.0.0.0; break; else wait 30; fi } done > /dev/null 2>&1 &
nginx -g "daemon off;"nginx.conf
user nginx;
worker_process auto;
pcre_jit on;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
type_hash_max_size 2048;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
}
}net.redbarrel.knowhow.conf
server {
listen 80;
listen [::]:80;
server_name knowhow.redbarrel.net;
root /srv/knowhow/public;
index index.html;
access_log /var/log/nginx/www_access.log;
error_log /var/log/nginx/www_error.log;
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bpm|rtf)$ {
access_log off; log_not_found off; expires max;
}
location / {
try_files $uri $uri/ =404;
}
}hugo-cron
* 4 * * * cd /srv/knowhow;/usr/local/bin/hugo --minifyThe last line of the cron file must be an empty line in order to satisfy the syntax of the cron file
Portainer Build
- Upload Dockerfile .tar to Portainer as hugo.custom
- Create a docker volume called hugodata
- Create a macvlan config network called br_knowhow_config
- subnet: <subnet>
- gateway: <gateway>
- IP range: <ip range>
- parent network card: <interface>.<vlan> (i.e. ens10.5)
- Create a macvlan network based on br_knowhow_config caled br_knowhow
- ☑ enable manual container attachment
- Create VLAN in your firewall and switches, tagging through to your container host
Portainer Stack / Docker Compose Definition
version: "3"
services:
hugo:
image: hugo:custom
volumes:
- hugodata:/srv
networks:
- br_knowhow
environment:
- SSHUSER=<username>
- SSHPASSWORD=<password>
volumes:
hugodata:
external: true
networks:
br_knowhow:
external:
name: br_knowhow