How I set up this Gemini host


For the moment at least, this host is running off a tiny computer of mine called "fruitwitch", a first generation Raspberry Pi that I've been using to host my twitter (and later, Mastodon) bots since 2014. Just in case it's useful, here are all the steps I went through to get it to serve the page you are currently reading. This took me about an hour and a half to figure out from start to finish.


If you're a Linux expert and spot something I could be doing better here, please let me know! I'm a videogame developer goofus and my knowledge of servers and such is very patchy.


The Gemini server software I run is called gmnisrv, it was created by Drew DeVault. It's written in C and uses very little CPU power, so it's ideal for running off a low-powered device.


gmnisrv project page


These first few steps are super specific to my ISP, hosting, and home network situation so unfortunately I don't really have any generally applicable instructions for you here in the next three paragraphs. Skip below that for stuff that may be more applicable.


I use a local ISP that is very good about user privacy and autonomy. I'm not sure if they offer "fixed IP" as a service but my network's external IP with them hasn't changed in the 6+ years I've used them, so I basically have a fixed IP. Very handy. Aside from running a Quake server a couple of times, I've never really used it for much before this.


Using the "Modify Host Records" feature on my domain host's control panel, I created a new entry that defines a subdomain called "gem" pointed to my Pi's IP. The result is that gem.vectorpoem.com now points to that IP, and if a Gemini client hits that location on the default port number 1965, the server will return something. I'm not running a webserver on any machine on my network (and have those outgoing ports firewalled) so accessing that same domain from a web browser does nothing.


Then there's the matter of allowing my server to communicate with the outside world via the appropriate port. Router UIs are too numerous and varied for me to describe how you'd do this in a generally useful way. Assuming your router has a browser-based configuration UI, connect to it and look for something like "port forwarding" in the options. You want to _allow_ TCP traffic over port 1965 for your server's internal IP, which will be something like 192.168.1.126 - that's not a public IP, those are just numbers your router uses to keep track of what's connected to it.


So then I needed to set up my actual server. Fruitwitch isn't plugged into a monitor, it's just connected to my router, and it doesn't have any GUI functionality installed. So I use ssh to connect to it and interact on the command line same as I would my Linux desktop machines. I'll write out commands I run at the command line.


ssh pi@fruitwitch


[hacker voice] I'm in. Now I want to get the gmnisrv code on this machine and navigate into its directory:


git clone https://git.sr.ht/~sircmpwn/gmnisrv

cd gmnisrv


Now I'll follow a simplified version of the instructions from gmnisrv's README for building and running the code:


cd build

../configure

make


This produces an executable gmnisrv binary in the ~/gmnisrv/build/ directory. The README-recommended build process involves then running "make install" to copy it to the machine's root filesystem, but due to some boring details about how I'm running this Pi, I'd prefer to keep everything in my home directory.


So now I need to create a configuration file called gmnisrv.ini that tells the server program what I need it to host. gmnisrv helpfully provides an example gmnisrv.ini, which I modify for the specifics of my site and server:


# Space-separated list of hosts
listen=0.0.0.0:1965 [::]:1965

[:tls]
# Path to store certificates on disk
#store=/var/lib/gemini/certs
store=/home/pi/gemini/certs

# Optional details for new certificates
organization=gmnisrv user

[localhost]
root=/home/pi/gemini

[gem.vectorpoem.com]
root=/home/pi/gemini/vectorpoem.com

Note that /home/pi/gemini/ is not the same directory that I cloned the gmnisrv code to. I leave the server in its own directory so I can wipe and recreate it if needed. /home/pi/gemini/ is where my config and site-specific stuff lives, it's what I want to copy off when I run a backup. This directory is where I create the gmnisrv.ini.


To run gmnisrv, I use this command line:

/home/pi/gmnisrv/build/gmnisrv -C /home/pi/gemini/gmnisrv.ini


And to save typing, I put that command line into a shell script and give it executable privileges:

echo "/home/pi/gmnisrv/build/gmnisrv -C /home/pi/gemini/gmnisrv.ini" > ~/bin/gemini.sh

chmod +x ~/bin/gemini.sh


The -C command line switch points gmnisrv to the custom config file I created instead of using its default in the root filesystem, which again I opted not to install to.


Within the gemini/ dir, I create the a subdir for this domain's files called vectorpoem.com. If I ever want to host another site, I'd create another subdir parallel to that. Within the vectorpoem.com dir, I create a mostly-blank "hello world" file called index.gmi, which is sort of like index.html on a web server - by default gmnisrv knows to serve that document if someone accesses the domain itself.


At this point, I can run gmnisrv and access index.gmi from both my local network and remotely! I want the server to run every time the Pi reboots, so I create a "cron job" (scheduled task):


crontab -e


and at the bottom of that file, I add this line:


@reboot /home/pi/bin/gemini.sh


From here, it's mostly down to building the site out from that first stub page. I do this from my desktop machine, using my text editor - Gemini is so simple you don't really need a WYSIWYG tool.


I keep my primary copies of all the pages on this site there in a subdirectory of my projects dir, the ones that end up on the server are simple copies. To deploy my latest files to fruitwitch, I use a recursive "scp" command, which is basically like ssh but for a one-off local-to-remote file copy operation. I created a script called pi_sync.sh to make this a one-liner:

scp -r vectorpoem.com/* pi@fruitwitch:/home/pi/gemini/vectorpoem.com/


"Recursive" there, specified with the -r switch, means that all files in any subdirectories I create for my site will get copied as well. It does mean that every file gets copied every time, even if it didn't change, but right now this site is tiny enough that it doesn't matter, and the nature of Gemini is that everything is very low bandwidth. If I ever want to serve larger files from here, I'll need to tweak my setup.


Being able to preview content locally without having to push work-in-progress files to the server is obviously valuable. So I build a local copy of gmnisrv and run it on my desktop machine. The only entry in gmnisrv.ini I need here is for "localhost", aka the name that always resolves to the local machine. Then I simply point my client to gemini://localhost.


That's about it! You can read about the particulars of authoring Gemini pages, which is _drastically_ simpler than HTML, in section 5 of the Gemini specification, titled "The text/gemini media type":


gemini://gemini.circumlunar.space/docs/specification.gmi



vectorpoem.flounder.online/