How to Host Your Own Gemini Site in the Cloud

Mon 8th February 2021 By David T. Sadler.

So I have a Gemini site over at gemini:// and I thought I'd write up how I achieved this in case anyone was interested in doing the same.

I would say that from purchasing the domain name to having a complete server hosting the site took about 30 minutes in total.

Purchasing a Domain Name

I decided that for the moment I would keep my traditional "Big Web" content hosted at and use a different domain name for my new Gemini site. Since this meant purchasing a new one I popped over to to acquire Side note: I used to own this but decided not to renew it for some crazy reason.

Creating a cloud sever

My cloud provider of choice is Hetzner and creating a new server is done in eight steps.

1. Location

Hetzner provide a few locations in Europe as to where the server is hosted. For this server I chose Helsinki.

2. Image

I chose Ubuntu 20.04 as the operating system as this is the one I'm most familiar with.

3. Type

As this server is only going to a host a Gemini site I don't need a overly powerful system so I chose their most basic CX11 configuration. For €2.99 a month this gives me:

4. Volume

You have the option of attaching additional storage to the server. I skipped this step as for the time been the 20GB SSD that comes with the server should be enough for my needs.

5. Network.

I skipped this step as its not needed.

6. Additional features

Again I skipped this step but select any if you believe that you will need them.

7. SSH Key

When a server is created a root user is added and a password is emailed to you so that you can login. However if you provide a SSH key it will be installed on the server instead of creating a password.

I like to use separate keys for each server that I manage so I tend store the them in a directory named after the hostname.

$ mkdir ~/.ssh/

$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/

The SSH key is added by clicking + ADD SSH KEY and then copying and pasting the contents of the file.

8. Name

I name my servers after the hostname so for this I called it I then created the server by clicking CREATE & BUY NOW.


Once the server was created I took the allocated IP address and ensured that I could access it via SSH using the key that I had provided.

$ ssh root@ -i ~/.ssh/

Resolve the Domain Name to the Server

In my account I went to the DNS Records section for the domain name I had purchased. There I deleted everything except for the @ (A) and www (CNAME) records which was configured as follows:

The A record is configured with the IPv4 address of my new server and the CNAME with the domain name. Note that the CNAME must end with a period!

After saving the changes it was just a matter of waiting for it to propagate through the DNS system. At which point I could use the domain name when logging in via SSH.

$ ssh -i ~/.ssh/

Securing the Server

At a bare minimum I setup a firewall and harden SSH. I may at a later date go further, such as installing fail2ban.

Configure a Firewall

This setup will deny any incoming requests unless they were first initiated by a request from the server. Since I need to be able to access the server I allow SSH. The Gemini protocol uses port 1965 so that is also allowed.

$ ufw default allow outgoing
$ ufw default deny incoming
$ ufw allow OpenSSH
$ ufw allow 1965
$ ufw enable

Harden SSH

I edited the /etc/ssh/sshd_config file.

$ vim /etc/ssh/sshd_config

I added the two below options so that the root user is not allowed to access the sever via SSH and other users may only access using keys.

PermitRootLogin no
PasswordAuthentication no

Since I'd made changes to the configuration I needed to restart the SSH service.

$ service sshd restart

Create non-root User

Whenever I access a server I like to login as a non-root user that is able to run sudo on the system.

$ adduser gemini

$ usermod -aG sudo gemini

As the SSH key is already on the server I can copy it to the non-root user account.

$ rsync --archive --chown=gemini:gemini ~/.ssh /home/gemini

On my local system I confirm that I can log in as the new user without a password.

$ ssh -i ~/.ssh/

I also confirm that I have sudo access.

$ sudo ls

Installing a Gemini Site and Server

Directory structure

I decided to go with a very simple directory structure. Each site will be a sub-directory in ~/sites that will be named after the domain name. Then each site will have the following sub-directories. The idea is that I may want to host more than one site in the future.

I created the directory structure with the below command.

$ mkdir -p ~/sites/{bin,certs,public,scripts}

Install certificates

Sine the Gemini protocol encourages using a self-signed certificate I installed one with the openssl command.

$ openssl req -x509 \
    -newkey rsa:4096 \
    -keyout ~/sites/ \
    -out ~/sites/ \
    -days 3650 \
    -nodes \
    -subj "/"

Create Some Test Content

I created a very simple index.gmi file purely for testing.

$ cat << EOF > ~/sites/
# Welcome

Hello world!

Install the Gemini Server Binary

I decided to go with agate as the Gemini server as its very simple to install and configure. Installing it was a matter of downloading the binary archive into the bin directory and setting the executable permission on it.

$ cd ~/sites/

$ wget

$ gunzip agate.x86_64-unknown-linux-gnu.gz

$ mv agate.x86_64-unknown-linux-gnu agate

$ chmod u+x agate

I wrote a very simple bash script to run agate and have it serve the site.

$ cat << EOF > ~/sites/

/home/gemini/sites/ \
    --content /home/gemini/sites/ \
    --key /home/gemini/sites/ \
    --cert /home/gemini/sites/ \
    --addr [::]:1965 \
    --addr \
    --hostname \
    --lang en-GB

$ chmod u+x ~/sites/

Testing the Site

At this point I have the Gemini server installed and a site available for testing.

I first started agate with the bash script.

$ ~/sites/

[2021-02-05T17:26:56Z INFO  agate] Listening on [[::]:1965,]...

With agate up and running I pointed my Gemini client to gemini:// and confirmed I was able to access the site before entering Ctrl-C to halt agate.

Configure Systemd

Since I was happy that agate was able to serve my new site I created a systemd unit to ensure that agate was started whenever the system was rebooted.

$ sudo vim /etc/systemd/system/agate.service

The unit is very simple and just runs the bash script to start agate once the network is available.

Description=Agate Gemini Server



I then started this service and confirmed it was working.

$ sudo systemctl start agate.service

$ sudo systemctl status agate.service

Active: active (running)

The final step was to have this service start when the system is rebooted.

$ sudo systemctl enable agate.service


Setting up a Gemini site was easy to do and I hope this guide shows it. I have several ideas about how I'm going to use this new site and I'm excited to see where this leads to.

Links - My domain registrar of choice.Hetzner - My cloud server provider.Agate - A simple Gemini - My Gemini site.Gemini - Read More Posts.

I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.



The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Copyright © 2021 David T. Sadler.

Return to Homepage.