Hosting Hugo on Google AppEngine
All my blogs are generated by Hugo and hosted on AppEngine. In this guide I’ll show you how to host a standalone blog or integrate it into your existing Golang webapp.
Hugo
Hugo is a static site generator for blogs. You write your articles in Markdown and export them to html.
To get started install the Hugo executable that comes with your operating system’s package manager.
Next, create a directory with the source code of your blog.
Now you can use hugo
to generate your html files.
It’s intimidating the first few days, but so is every new tool. Get started with the quick start guide, pick a cool theme and continue with the rest of this guide.
A word about Hugo themes
The official documentation advises you to add themes as a git submodule. But the themes rarely get updated once released and a submodule makes it harder to edit them.
Copying the theme’s files into your project will make your life easier.
AppEngine
Google AppEngine allows you to run a Golang webapp. Their free tier is very generous, you can host one instance per project under your own domain at no cost.
Option 1: Part of an app
You have a Golang webapp serving example.com/
and want to host your blog under example.com/blog/
.
There are two options for where to store the source.
As a git submodule
To store your blog in a separate git repository add a new git submodule to your main project and clone it under blog/
.
Your output files will reside in blog_public/
.
Add this to your Makefile
to update the contents:
blog:
git submodule update --remote --merge
(cd blog && hugo --cleanDestinationDir --destination ../blog_public)
Inside the main project’s tree
Keep your Hugo sources under blog/
and your output files in blog_public/
.
Add this to your Makefile
to update the contents:
blog:
(cd blog && hugo --cleanDestinationDir --destination ../blog_public)
Setting up the webhandler
If you use Gorilla Mux, edit your main Router to look something like this:
func Router() *mux.Router {
r := mux.NewRouter()
// Handle duplicate content, for SEO. /blog -> /blog/
r.Path(urls.Blog).HandlerFunc(
func(writer http.ResponseWriter, req *http.Request) {
http.Redirect(writer, req, urls.Blog+"/", 301)
},
)
r.StrictSlash(true)
// ...
// Blog
s := http.StripPrefix(urls.Blog, http.FileServer(http.Dir("blog_public")))
r.PathPrefix(urls.Blog).Handler(s)
return r
}
And that’s it.
Option 2: Standalone Blog
You don’t have a webapp and just want to host a blog.
Some people advise a simple file hosting service like Amazon S3 or Google Cloud Storage, so why complicate it with AppEngine? AppEngine is still a better choice than Amazon S3 or Google Cloud Storage, because you can control redirects.
Here are the steps:
- Create a new AppEngine project.
- Fork this skeleton repository.
- Edit the
Makefile
and setPROJECT
to the project’s name. - Put your hugo sources into
hugo/
. - Type
make hugo
when working on your blog. - Type
make deploy
to publish it.
Your new blog will have a /blog/
prefix in the URL.
That’s in case you change your mind and want to host something more under that domain in the future.
The app.yaml
file limits autoscaling to at most one instance, making sure the hosting stays free:
service: default
runtime: go113
automatic_scaling:
max_instances: 1
handlers:
- url: /.*
script: auto
secure: always
Configuring your blog
Now that you have your hosting sorted out it’s time to make several technical decisions. How to set the URL structure? How to optimize for SEO? Is RSS still relevant? Make sure you’re making informed decisions by learning how to start a blog.