Gemini Mirror from Hugo

Let's check out how to host a gemini capsule based on the content you have in hugo already!

For this little tutorial, we'll use the Hugo Getting Started guide and create a small blog with exactly one blog post and then start serving it over gemini://.

The markdwon to gemtext conversion and serving will be done through some GO packages, so remember to install go.

Setting up Hugo

Firstly we'll set up hugo, either through your package manager or through the GO toolchain. Open up a terminal and run the default tutorial instructions:

hugo new site quickstart
cd quickstart
git init
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke
echo "theme = 'ananke'" >> config.toml
hugo server

Now we also need some example content, so we'll add my-first-post.md to the content directory:

---
title: "My First Post"
date: 2022-11-20T09:03:20-08:00
---
## Introduction

This is **bold** text, and this is *emphasized* text.

Visit the [Hugo](https://gohugo.io) website!

now we should see the post show up at localhost:1313. So far so good!

Serving Gemini from Hugo Content

There are three related projects all by n0x1m that are going to enable us to serve gemfiles from the existing hugo content. What they did is to grab some hugo code from a few versions ago to generate an identical path tree and they also serve it over gemini, perfect, right?

With the go toolchain installed you can run:

go install github.com/n0x1m/hugoext@latest
go install github.com/n0x1m/md2gmi@latest
go install github.com/n0x1m/gmifs@latest

Now we can run:

hugoext -ext gmi -pipe md2gmi

and it will build the .gmi file output in the public directory \o/.

Next, to serve the content we can run:

gmifs -autoindex

and visit: gemini://localhost

What we should see in a gemini browser is this:

Brilliant, we can conclude it works!

Now we just need to deploy it somewhere and point a domain at it.

Deploying hugoext / gmifs with Docker

I'm used to Docker at this point and I don't have the nerve to learn 100 different hosting providers own config syntax, so we'll write a small Dockerfile.

One of the providers I've been wanting to try out is fly.io, but you can obviously deploy anywhere where Docker images are supported, like heroku, AWS, Azure or whatever you can think of.

FROM golang:1.19-alpine

# install GO dependencies
RUN go install github.com/n0x1m/hugoext@latest
RUN go install github.com/n0x1m/md2gmi@latest
RUN go install github.com/n0x1m/gmifs@latest

RUN mkdir -p /var/site
WORKDIR /var/site

COPY content /var/site

EXPOSE 1965

CMD ["gmifs", "-autoindex", "-autocertvalidity=666"]

In the CMD line we add "-autocertvalidity=666"

because by default our certificate the self-signed cert would only be valid for 1 day:

-autocertvalidity int
        valid days when using a gmifs provisioned certificate (default 1)

Now let's build it with docker build . -t hugo-gmi and run with:

docker run --init -p 1965:1965 -t hugo-gmi

which maps the exposed port to the same post on the host system. If you get an error, it's because you still have gmifs running on the same port in another terminal.

Accessing gemini://localhost should now again show you your website.

Deploying Docker Image to fly.io

If you want to use fly.io, head on over and create an account first, you'll need to punch in your credit card if you want to deploy.

You'll get some things for free, but be vary of excess bandwidth you might have to pay for:

Up to 3 shared-cpu-1x 256mb VMs 3GB persistent volume storage (total) 160GB outbound data transfer

First we need to install the [flyctl][flyctl], for me that is:

curl -L https://fly.io/install.sh | sh

Then you will want to authenticate your flyctl to your fly.io account, so you run:

flyctl auth login

which will open your browser and let you authenticate the request, after that you're logged in on your system.

Next, to trigger the interactive dialogue for deployment, we run the following in our quickstart directory or where your gmi-hugo blog is located.

flytcl launch

We answer a few questions, but the defaults are pretty good. We need to answer with y when it comes to if we actually want to deploy.

flyctl region selection

Now we can watch some terminal output of our image being built in production, after it's done we can go to our fly.io dashboard, find our app and access the random subdomain with our gemini browser!

fly.io dashboard and lagrange

Now you just need to grab your DNS settings and point gmi.yourdomain.com at this IP address or host to make sure you can be reached at the desired address!

Closing Thoughts

I got incredibly lucky finding the awesome projects thata are gmifs and hugoext because they were exactly my use case. Trying out fly.io was very pleasant and really perfect for tiny projects and niche blogs!

Your deploy process might vary, you might already have a Dockerfile and might just want to copy your content directory to a different project directory that will only have the dockerfile above to push to a separate remote.

For my own blog I had to use a slightly different approach to get my content in shape to be served through gemini://jonathanmh.com, because I don't personally use hugo.

Tagged with: #gemini #hugo

Thank you for reading! If you have any comments, additions or questions, please tweet or toot them at me!