2 minutes
Self-hosting Hugo
This is the first post on my new hugo
blog which is being hosted on my
Hetzner dedicated server. Amongst others the server is running traefik and
minio as Docker containers. Both services build the backend for the blog.
I went to these steps to run the blog.
- Created a bucket on minio to store the blog content.
- Created a user and a policy to deploy the blog with the
hugo
cli. - Added a deployment target in the
hugo
site configuration. - Setup a traefik router, a service, and several middlewares to serve the blog.
The Minio setup
Create the bucket, the user, and the policy with the minio client.
mc mb coresec/blog.tschaefer.org
mc admin user add coresec hugo secret
mc admin policy create coresec blog blog.json
mc admin policy attach coresec blog --user hugo
The blog policy looks like this.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::blog.tschaefer.org/*"
]
}
]
}
Add a deployment target in the hugo
site configuration.
deployment:
targets:
- name: production
url: s3://blog.tschaefer.org?endpoint=https://minio.coresec.zone
Deploy the blog.
AWS_ACCESS_KEY_ID=hugo \
AWS_SECRET_ACCESS_KEY=secret \
AWS_DEFAULT_REGION=ger-south-gap-1 \
hugo deploy
The Traefik setup
Create a TLS terminated router and redirect all requests to the minio service.
routers:
blog:
rule: Host(`blog.tschaefer.org`)
service: minio
middlewares: path
tls:
certResolver: letsencrypt
services:
minio:
loadBalancer:
servers:
- url: http://minio-service:9000
For the usage of the bucket and to keep pretty URLs, the request path has to be modified by several middlewares.
First add the bucket name as a prefix to the request path.
bucket:
addprefix:
prefix: /blog.tschaefer.org
If the request path is the root path, add the main index.html
.
root:
replacepathregex:
regex: ^/$
replacement: /index.html
In any other case remove a trailing slash.
slash:
replacepathregex:
regex: ^(.*)/$
replacement: "${1}"
To any request path not ending with a file extension add index.html
.
index:
replacepathregex:
regex: ^(/(?:[\w-]+/)*[\w-]+)$
replacement: ${1}/index.html
For error handling, redirect to the main error page.
error:
errors:
status: "404"
query: /blog.tschaefer.org/404.html
service: minio
Chain all middlewares together and use them in the above router.
path:
chain:
middlewares: bucket,root,slash,index,error
Conclusion
The blog is up and running. I learned a lot, especially about Traefik and it’s powerfull middleware system. I’m looking forward to writing more posts and improve the setup over time.
hugo self-hosting traefik minio
371 Words
2025-03-23 20:31 (Last updated: 2025-03-23 23:05)
1d72ba6 @ 2025-03-23