Skip to content

Instantly share code, notes, and snippets.

@NatoBoram
Last active September 19, 2024 13:37
Show Gist options
  • Save NatoBoram/09d244ab02af16fecb62b917f7bee3c0 to your computer and use it in GitHub Desktop.
Save NatoBoram/09d244ab02af16fecb62b917f7bee3c0 to your computer and use it in GitHub Desktop.
The complete guide to hosting a public IPFS gateway

Hosting a public IPFS gateway

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!

Get a host

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!

DigitalOcean Referral Badge

Get a domain name

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.

Setup the server

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.

Create an IPFS user

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.

Compiling Go

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.

Compiling IPFS

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

Adding a service

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.

Using the domain name

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.

Nginx

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.

HTTPS

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.

Subdomain gateway

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.

DNSAddr

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.

Abuse

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.

@ManishNirmal
Copy link

Hi I am Manish
I would like to deploy an ipfs public gateway at home machine.
Please help me with it.
I can pay you for helping me to deploy!
Thanks

@hvuhsg
Copy link

hvuhsg commented Aug 16, 2023

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment