Self-hosting a Website with Caddy
Published: 2025-04-16 21:46 +1200
Last Modified: 2025-04-18 16:25 +1200
Background
After getting my Gemini server up and running, I wanted to take the next step and host a website in the same manner. After a few fumbles with Apache HTTP Server, I was encouraged by John Wq to give Caddy a go as it is a bit more user-friendly. It was just what I needed for my beginner level. I still fumbled a bit, but the documentation was a lot more helpful, and I noticed some things along the way that were probably preventing things earlier as well.
I also wanted to try out the new Bash Static Site Generator (BSSG) because it's new, it's bash, and it's got some pretty rad custom themes out of the box. I already have a website where I muck around with styles, and given this one is going to be specifically for notes, I'm happy to not faff around with styling here. Other static site generators I've been weighing up are manpageblog and mdsite, both very simplistic and in line with where I'd like to take this site. For now though, BSSG it is.
Update 2025-04-18: I have chosen MkDocs with the Material for MkDocs plugin as my static site generator. The modern styling, top-notch documentation and easy of use won me over in the end. It is free, and open source.
Method
Install Caddy
Caddy is available in the apt package manager:
sudo apt install caddy
Add DNS record
In my namecheap.com dashboard, go to Domain List --> Manage (zkbro.com) --> Advanced DNS.
If it's not already enabled, enable Dynamic DNS - This ensures the IP address I assign to this host are updated dynamically if they ever change (which they do). This also requires a Dynamic DNS client to pass my current IP address to namecheap regularly. It is optional, but saves me having to manually update the public IP in the dashboard everytime it changes.
Under "Host Records" add a new record with the following information:
- Type: A + Dynamic DNS
- Host: notes (as I want my website to be notes.zkbro.com)
- Value: My current public IP address
- TTL: Automatic
Install ddclient (a Dynamic DNS client):
sudo apt install ddclient
Follow the basic configuration steps in ddclient and when prompted use the password generated back in the namecheap dashboard.
Ensure the /etc/ddclient.conf file looks something like this:
# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf
protocol=namecheap \
use=web, web=ipify-ipv4 \
login=zkbro.com \
password='DDNS_PASSWORD' \
gmi,notes
Note I added "notes" so both gmi and notes subdomains get updated.
Forcing an IP update is a good way to test it is working:
sudo ddclient -verbose -force
Allow ports through firewall
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
Set port forwarding
Log in to my router GUI at http://192.168.1.1/. In my old Slingshot ISP router menu go to "Advanced Setup" --> "NAT" --> "Virtual Servers" and add a new server with the following configuration:
- Use interface: ETH WAN
- Custom Service: Caddy (or whatever)
- Enable LAN Loopback: true (so I can access the site on my own LAN).
- Server IP Address: 192.168.1."18" (replace "18" with whatever the server machine is. Check with
ip a
.) - Status: Enable
- External Port Start: 80 & 443
- External Port End: 80 & 443
- Protocol: TCP
- Internal Port Start: 80 & 443
- Internal Port Start: 80 & 443
My understanding is that both ports are needed so both http(80) and https(443) can be used.
I got an error when adding port 80 as it was reserved for the router's own interface. I had to go to "Management" --> "Access Control" --> "Services Control" and change the HTTP LAN Port number from 80 to something else to free it up. It means I now have to access my modem via http://192.168.1.1:8080/ now. No biggy.
Create a website
I will not lay out the details here as there are many ways to do it, but for now I am simply following the Material for MkDocs documentation.
Create a Caddyfile
Once the website has been built, navigate to the website's public root directory and create a file called Caddyfile. It will contain the following text:
notes.zkbro.com
file_server
This tells Caddy it is a static website. Then run caddy as a daemon:
sudo caddy start
To stop:
sudo caddy stop
Instead of creating a Caddyfile, I can just use:
sudo caddy file-server --domain notes.zkbro.com
Next steps
Fix my workflow. I'm afraid this is going to be a big thing as I've now got 2 websites, a gemini capsule, a laptop and raspberry pi, self-hosted (pi), hosted (neocities), git repositories, text editors, md2gemini converter, tmux, termux, zellij, tailscale, ssh, scp, custom blog scripts, obsidian, logseq, helix, random project folders with md and gmi files, template files, and a bunch of other things to think about. I'm actually looking forward to getting these things a bit tighter.A mind map of all these things will be my starting point.