💾 Archived View for josipantolis.from.hr › setup.gmi captured on 2023-05-24 at 17:36:05. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-11-30)
-=-=-=-=-=-=-
My gemini pod is self-hosted on a "work from home" Raspberry Pi 2. This post is equal parts reminder for myself of what I did to get it running and a guide for anyone else interested in self-hosting a gemini pod.
Not many people know this, but in my home country of Croatia all citizens are entitled to a free domain. For private individuals it is a subdomain on from.hr domain. It has to be related to your name, and it's linked to your personal ID number (so you can only get one). I registered for it over at:
There are a couple of gotchas: it needs to be renewed on a yearly bases (but this comes down to clicking a link from an automated reminder email, so it's not a bit deal). Next issue is that while domene.hr provides a way to register the domain it is not a DNS name server. The only DNS record type that it supports is NS. So I still needed a name server.
Continuing the theme of "getting this pod running for free", I looked around for a free name server. I found:
They offer name server configuration in their free tier. I'm no expert in DNS services, but since namecheap had all I need I went with them. I configured following NS servers records for my domain on my domene.hr config page:
Listing all 5 of them might be an overkill, but OK.
Now I needed to configure a dynamic DNS (since my pod is hosted over my home internet connection my public IP can change at any moment). I've previously used a duckdns.org service for this, and it worked well. My home router has one click support for reporting its IP to duckdns. However, introducing another DNS service into the mix would complicate my setup. Luckily namecheap supports dynamic DNS in their free tier. The only problem here is that their "officially supported client" is a Windows program and I wanted this project to run on Linux. However, the actual IP update can be achieved with a simple HTTP GET request:
Weirdly titled "How do I use a browser to dynamically update the host's IP?" docs page
I wrote a simple shell script to do it:
#!/usr/bin/env bash # Content of /usr/local/bin/ddns-update.sh HOST='@' DOMAIN='josipantolis.from.hr' PASSWORD='password provided by namecheap' IP=$(curl -s ipinfo.io/ip) curl -si "https://dynamicdns.park-your-domain.com/update?host=$HOST&domain=$DOMAIN&password=$PASSWORD&ip=$IP"
Since I wanted this script to run periodically and I wanted to learn a little bit of systemd along the way, I defined a service for this script:
# Content of /etc/systemd/system/ddns-update.service [Unit] Description=Update dynamic DNS for domain josipantolis.from.hr with namecheap NS. Wants=ddns-update-schedule.timer [Service] Type=oneshot ExecStart=/usr/local/bin/ddns-update.sh User=pi Group=pi [Install] WantedBy=multi-user.target
Lastly, schedule is defined as a systemd timer:
# Content of /etc/systemd/system/ddns-update-schedule.timer [Unit] Description=Schedule for updating dynamic DNS record for domain josipantolis.from.hr on namecheap NS. Requires=ddns-update.service [Timer] Unit=ddns-update.service OnActiveSec=5min OnUnitActiveSec=5min [Install] WantedBy=timers.target
This way my pi should report its public IP every 5 minutes, meaning I shouldn't have too long outages when my IP changes. I can check back on how it's doing with:
journalctl -f --unit ddns-update.service
I have this Raspberry Pi 2 at home, I think a colleague once gave it to me (if you ever see this, thank you Pero!). I used to run retropi on it for a while. I even used it as a PC for a couple of days. But most of the time it used to just sit judgmentally on my desk and collect figurative dust. No more.
I installed a light version of Raspberry Pi OS (Raspbian) on it and plugged it into my router. In order to use it as a server I needed it to have static IP inside my local network. Since my router doesn't support static leases I defined it on the pi side with an update to the /etc/dhcpcd.conf file (just gotta be sure to pick an IP that's outside the DHCP lease pool defined on the router).
To make things easier to remember I named my pi gemini-pod it its own /etc/hostname file but also on my laptop from where I usually access it over SSH (that's in /etc/hosts file). This is a little bit of manual configuration but is good enough for me.
Last thing to set up on the router side was port forwarding. Nice thing about using a dedicated protocol like gemini is that it runs on a distinct default port (1965 in this case) so it is easy to route traffic.
For gemini server I used Agate:
I learned how to set it up in this great intro to gemini YouTube video:
How to make a Gemini Capsule ...
Future me (or dear reader) if you forgot how to do this go watch the video! For reference, here's my .service config file:
# Content of /etc/systemd/system/gemini-server.service [Unit] Description=Service running agate gemini server, serving my pod on josipantolis.from.hr domain. After=network.target [Service] ExecStart=/home/cosmonaut/bin/agate --content /home/cosmonaut/pod/ --hostname josipantolis.from.hr Restart=always WorkingDirectory=/home/cosmonaut User=cosmonaut Group=cosmonaut [Install] WantedBy=multi-user.target
Quick note: since version 3.0.0 agate generates missing server certificates on startup. This is an improvement from the video, further simplifying the setup.
I can check up on my gemini server with:
journalctl -f --unit gemini-server.service
I created a separate user, cosmonaut, to run agate under. I upload my pod content with same user (to /home/cosmonaut/pod directory). There's nothing fancy about this, I just sftp files from whatever machine I'm typing this on up to the pi.
A few free services, approximately half a day to set it all up, a hand-me-down tiny computer. And decades of free open source development that enables and powers all of it. It might not be the best use of FOSS ever nor the biggest achievement of my life, but I like it. 🖖️