This guide assumes that you've used ssh
before. If not, then you must read How to Set Up SSH Keys.
I'm also going to assume you've used ipfs
before. If not, then take some time to discover IPFS with IPFS Desktop, IPFS Companion and go-ipfs
!
A public IPFS gateway can be hosted at home or on the cloud.
If you want to host at home, be aware that it will costs electricity and bandwidth. You can host it on an upside-down laptop running Ubuntu Server or even on a Raspberry Pi.
If you want to host it on the cloud, I recommend Digital Ocean since I've had good personal experiences with it, but really, any host can do. Digital Ocean's 5$ tier is enough to get you started!
If you want a subdomain gateway, you'll need to use one that's compatible with Let's Encrypt's wildcard plugins. Or at least use one that allows you to change its name server to Digital Ocean, which is compatible.
Personally, I used Google Domains, but it's a bit expensive. Plus, it's not compatible with Let's Encrypt wildcards, so I had to move over the management of my domain to Digital Ocean. There are free alternatives like freenom if you're not looking for a fancy name.
Alright, so you've got a machine capable of running Linux and a domain name that you can use with certbot
. Now we need to put IPFS and a web server on this thing.
Personally, I prefer to compile Go and IPFS, but you don't have to. It can give some performance gains by better using the CPU it's being compiled on and using more up-to-date versions of Go can offer better performance and security. Since this guide focuses on commodity hardware (or 5$ cloud), any performance we can get is beneficial.
For better security, let's isolate the IPFS process into its own user account.
adduser ipfs
Once the user is created, you can switch to that account just as easily.
su ipfs
Next, we'll compile Go and IPFS on that user's account and setup a systemd service for IPFS.
This might be a problem if you're using that 5$ Digital Ocean node since compiling Go requires 1.5 GiB RAM. You can fix this by adding 1 GiB of swap, but don't keep it on all the time. Refer to this guide if you need help setting up swap.
Warning: Although swap is generally recommended for systems using traditional spinning hard drives, placing swap on SSDs can cause issues with hardware degradation over time. Due to this, we do not recommend enabling swap on any other provider that uses SSD storage.
Get a somewhat recent Go toolchain easily accesible via a package manager. It doesn't need to be the latest version since it'll only be used to compile that latest version. I personally put drop-in applications in ~/Applications
and add them to .profile
. The full guide on how to compile Go is here, but here's the gist of it.
apt install git gcc
snap install go --classic
mkdir -p ~/Applications
git clone https://go.googlesource.com/go ~/Applications/go
cd ~/Applications/go/src
git checkout go1.18
./make.bash
Make sure to replace go1.18
with the latest Go version.
Edit your ~/.profile
to access this new Go installation.
# Go
PATH="$HOME/Applications/go/bin:$PATH"
PATH="$HOME/go/bin:$PATH"
After it's edited, source ~/.profile
and go version
should output something like go version go1.18 linux/amd64
.
With Go compiled, it's time to tackle IPFS. The instructions are here, but here's the gist of it. I'm enabling GOTAGS=openssl
in order to squeeze more performance out of it.
apt install make pkg-config libssl-dev libcrypto++-dev
mkdir -p ~/Applications
git clone https://github.com/ipfs/go-ipfs.git ~/Applications/ipfs
cd ~/Applications/ipfs
go get github.com/lucas-clemente/quic-go@go118
GOTAGS=openssl make install
IPFS offers a great deal of systemd service templates here, but I'm using a much more basic one.
[Unit]
Description=InterPlanetary File System (IPFS) daemon
Documentation=https://docs.ipfs.io/
After=network.target
[Service]
Type=notify
ExecStart=/home/ipfs/go/bin/ipfs daemon --enable-gc=true --migrate=true
ExecStop=/home/ipfs/go/bin/ipfs shutdown
Restart=on-failure
KillSignal=SIGINT
[Install]
WantedBy=default.target
This service is suitable for a user account while IPFS' templates are more suitable for system-wide installations.
Now it's time to enable this service!
ipfs init --profile=lowpower,server --empty-repo
systemctl --user enable ipfs
systemctl --user start ipfs
systemctl --user status ipfs
loginctl enable-linger $USER
If you don't enable-linger
, then the systemd service will close when you'll end your session. Try it a few times just to make sure it's still enabled even if you logout.
To link your domain to your web server, you'll need to manage the domain and add a few records. Let's say that my domain name is example.org
. I want to reuse it for other things, but I want ipfs.example.org
and ipns.example.org
to be linked to the IPFS node. This lets me use other subdomains for other activities.
Here's a bunch of records I needed to setup.
Type | Hostname | Value |
---|---|---|
A | *.ipfs.example.org | 203.0.113.123 |
A | *.ipns.example.org | 203.0.113.123 |
A | ipfs.example.org | 203.0.113.123 |
A | ipns.example.org | 203.0.113.123 |
AAAA | *.ipfs.example.org | 2001:db8:cad:d0::e89:2001 |
AAAA | *.ipns.example.org | 2001:db8:cad:d0::e89:2001 |
AAAA | ipfs.example.org | 2001:db8:cad:d0::e89:2001 |
AAAA | ipns.example.org | 2001:db8:cad:d0::e89:2001 |
CAA | ipfs.example.org | 0 issue "letsencrypt.org" |
CAA | ipns.example.org | 0 issue "letsencrypt.org" |
If I had used example.org
in its entirety for the purpose of the IPFS gateway, I wouldn't have to specify half of these. It's up to you if you want to use the whole domain on that machine or not.
The CAA records is to request that only Let's Encrypt create HTTPS certificates for this domain. It's inherited by subdomains, so technically just making one for example.org
would've been enough.
So now ipfs.example.org
redirects to a valid computer with a valid IPFS gateway on it. It's time to make it a bit prettier, otherwise it'll have to be used with ugly paths such as http://ipfs.example.org:8080/ipfs/
. You can return to the root
user to perform these changes.
apt install nginx
If you look at /etc/nginx/sites-available/default
, you'll notice this bit of config:
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
#}
Aaaaand let's do that! Copy that snippet to something like /etc/nginx/sites-available/ipfs
and let's make our configs there.
server {
server_name ipfs.example.org ipns.example.org;
location /ipfs {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host example.org;
}
location /ipns {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host example.org;
}
}
server {
server_name *.ipfs.example.org *.ipns.example.org;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
This config allows you to use /var/www/html/
to put some static HTML files in case someone ends up on your node's root. It's actually important to separate the normal and the wildcard server blocks because of the different proxy_set_header
configuration. It'll be important in a few steps, when we'll enable the subdomain gateway settings on the IPFS node. The IPFS node will only issue a redirect if the host matches the part without ipfs.
and ipns.
, so that's why we're re-writing it in the top server block.
There's no secret here, you have to go to https://certbot.eff.org/ and follow the steps there. Make sure to select wildcard instead of default and follow the step. Here's a sneak peek though:
snap install certbot --classic
snap install certbot-dns-digitalocean
certbot --dns-digitalocean --dns-digitalocean-credentials ~/.secrets/certbot/digitalocean.ini \
--installer nginx \
-d ipfs.example.org -d ipns.example.org \
-d *.ipfs.example.org -d *.ipns.example.org
Congrats, you're on HTTPS! Now let's go for HTTP2. HTTP2 enables querying multiple files at the same time, which is always useful.
You can find a configuration generator at ssl-config.mozilla.org. It'll conflict with certbot
in a few places, though. Some settings are handled by certbot
in /etc/letsencrypt/options-ssl-nginx.conf
and some are handled in /etc/nginx/sites-available/ipfs
. Just be careful to not break anything when you're doing changes :)
To use HTTP2, the important lines are listen 443 ssl http2; listen [::]:443 ssl http2;
, the rest is related to how clients and the Nginx server communicate with HTTPS.
Back to the ipfs
user. To enable the subdomain gateway feature, you'll need to edit Gateway.PublicGateways."example.org"
in ~/.ipfs/config
{
"Gateway": {
"PublicGateways": {
"example.org": {
"UseSubdomains": true,
"Paths": [
"/ipfs",
"/ipns"
]
}
}
}
}
Since the hostname is example.com
, it'll never match since this gateway is configured to use https://ipfs.example.org/ipfs/
in order to liberate the root domain for other activities. This is why the Host
header needs to be rewritten in Nginx a few steps above.
While we're here, you can enable add proxy_set_header X-Forwarded-Proto "https";
in Nginx to make sure the redirection happens on HTTPS.
If you want people to be able to connect to your gateway via ipfs swarm connect /dnsaddr/ipfs.example.org
, then you need to add some TXT records. You can type ipfs id
to get the list of addresses associated with your node.
Type | Hostname | Value |
---|---|---|
TXT | _dnsaddr.ipfs.example.org | "dnsaddr=/ip4/203.0.113.123/tcp/4001/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" |
"dnsaddr=/ip4/203.0.113.123/udp/4001/quic/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" | ||
"dnsaddr=/ip6/2001:db8:cad:d0::e89:2001/tcp/4001/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" | ||
"dnsaddr=/ip6/2001:db8:cad:d0::e89:2001/udp/4001/quic/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" | ||
TXT | _dnsaddr.ipns.example.org | "dnsaddr=/ip4/203.0.113.123/tcp/4001/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" |
"dnsaddr=/ip4/203.0.113.123/udp/4001/quic/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" | ||
"dnsaddr=/ip6/2001:db8:cad:d0::e89:2001/tcp/4001/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" | ||
"dnsaddr=/ip6/2001:db8:cad:d0::e89:2001/udp/4001/quic/p2p/12D3KooWPcii9P4riLHAQM7i673wX9hmNK3wUwLaSUZkdUTNXm34" |
Once these records are added, you should be able to use ipfs swarm connect /dnsaddr/ipfs.example.org
and ipfs swarm connect /dnsaddr/ipns.example.org
, which will both do the exact same thing.
As u/CorvusRidiculissimus pointed out, people will abuse your gateway. Luckily, IPFS has a blocklist of IPFS hashes already that you can use. It probably needs to be updated to support subdomain gateways, though.
cd /etc/nginx/snippets
wget https://raw.githubusercontent.com/ipfs/infra/master/ipfs/gateway/denylist.conf
Once downloaded, you just have to add this bit to your server block.
include snippets/denylist.conf;
And there you have it! It should be mostly configured. The part with the subdomain gateway and the dnsaddr really fucked with me until I managed to make them work. Also Google Domain and certbot didn't work together but I didn't know so I kept trying. Sigh.
Anyway, subdomain gateways are kinda a pain in the ass to setup. Have fun.