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:

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.

See also