How to create a static website on IPFS ?

We will create a static site using Hugo, then make this site available via IPFS using two methods:

  • IPNS
  • DNSLink (domain name required) Once the IPFS daemon is installed and configured so that the gateway can only provide pinned content, we will use Nginx as a reverse proxy on our gateway. Finally, we will use certbot to obtain a certificate via Let’s Encrypt.

Prerequisites


We will perform the basic installations of these different programs as we go along:

  • Hugo
  • IPFS
  • Nginx
  • Certbot

Hugo


Before anything else, our first goal is to create a static site using a template on Hugo.

Creation of the architecture

To do this nothing could be simpler, we will generate the architecture of our site using the command :

hugo new site website-static
cd site-static

Tip

To better understand the architecture of Hugo :

$ tree -d -L 1
├── archetypes ## markdown header generated by default
├── content ## location of .md pages
├── data 
├─── layouts
├─── public ## The generation of the static site
├── resources
├── static
└── themes ## the theme that will be set on the site.

Add theme

Next, we will add a theme to our site. For this example we will use PaperMod but you can choose other templates. Copy or clone the theme to the theme folder.

git clone https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

Note

More information about the installation and functionality of Papermod

Finally, add or adapt the theme field in the config.toml configuration file with the name of the chosen theme.

echo "theme = 'PaperMod'" >> config.toml

Now that we have the architecture of our site and the chosen theme, we will start the server which will be available by default at localhost:1313.

hugo serve -D --bind=0.0.0.0

The –bind=0.0.0.0 option allows us to get the result on the public IP address of the VPS as well as on the local address.

If you are satisfied with the result, we can move on to the IPFS installation step to place our rendering on IPFS.

Tip

  • Add pages to our site using the hugo new <filename>.md command. Update archetypes/default with draft: false to make the pages visible. The result of our site is in the public folder.

IPFS


Installation

Now that our site is ready, we will install kubo. Kubo is the most advanced implementation of IPFS in GB and is also the most advanced in terms of functionality. You can find the documentation for the installation of kubo on the different OS and take the opportunity to install golang which will be necessary.

For the installation of kubo we will clone the repo and compile / install the project.

# Clone the repo
git clone https://github.com/ipfs/kubo.git

# compile and install
make install

Initialization

We can now initialize our node’s configuration files in server mode.

ipfs init --profile server

Note

A ~/.ipfs folder must have been created. You can configure your node if necessary in ~/.ipfs/config.

Creating the IPFS service

We will now need to create a systemd service on our machine so that our node can be started on boot/restart and our site is always provided by our node within the IPFS network.

We will create a ipfs-daemon.service file in /etc/systemd/service/.

[Unit]
Description=IPFS daemon
After=network.target

[Service]
User=<USER>
Environment=IPFS_PATH=/home/<USER>/.ipfs
ExecStart=/usr/local/bin/ipfs daemon --init --migrate
StandardOutput=journal
Restart=on-failure
KillSignal=SIGINT

[Installer]
WantedBy=multi-user.target

Now that our service is created in systemd, we will start the systemd configuration manager, which will allow the system to take into account the changes and create a new dependency tree.

sudo systemctl daemon-reload

Next, we can enable and start our ipfs-daemon service.

sudo systemctl enable ipfs-daemon
sudo systemctl start ipfs-daemon

Tip

Check the service logs journalctl -u ipfs-daemon.service >

Add the site to IPFS

Now that our node is up and running, we can add our site. To do this, we’ll add the public folder we created earlier with Hugo.

ipfs add -r public/

We can see a bunch of CIDs, the one we are interested in is the root CID linked to the public folder. This CID will be useful because we will provide it through the IPNS property. This will allow us to always have the same link, while changing the content of our site.

ipfs name publish ${CID}

The result is the IPNS key that we can resolve to check which content is actually pointed to by the IPNS.

ipfs name resolve <keys>

Note

You should get /ipns/${CID}

Configuring the node

Our last step is to configure our node to only provide what is on the local node. To do this, we’ll update our configuration.

ipfs config --json Gateway.NoFetch true

Next, we restart our service

sudo systemctl restart ipfs-daemon

Tip

Don’t forget to run ipfs repo gc to release unpinned content.


Nginx


Now that our ipfs node is running and delivering only local content, we will set up a reverse proxy on our local IPFS gateway.

Installation

The installation depends on the Linux distribution. We will use Ubuntu as an example.

sudo apt-get install nginx

Configuring our proxy

We will copy the file /etc/nginx/sites-availables/default to edit it in order to access our gateway from the internet.

cp /etc/nginx/sites-availables/default /etc/nginx/sites-availables/ipfs-website
## ipfs-website
server {
    listen 80 ;
    listen [: :]:80 ;

    location /ipfs {
        proxy_pass http://localhost:8080 ;
        proxy_set_header Host $host ;
        proxy_cache_bypass $http_upgrade ;
        allow all ;
    }

    location /ipns {
        proxy_pass http://localhost:8080 ;
        proxy_set_header Host $host ;
        proxy_cache_bypass $http_upgrade ;
        allow all ;
    }
}

The configuration will allow us to make calls to our node for paths starting with /ipfs or /ipns. This will allow us to access the site by the CID or IPNS key.

Once we have edited the file, we will check for errors and take the changes into account for nginx.

sudo nginx -t
sudo systemctl reload nginx

Now you can try to access your gateway and access your site via IPNS.

http://<your-ip>/ipns/<key>

Note

Feel free to test with a CID that is not stored locally to verify that the content cannot be retrieved from outside.


Certbot


We have access to our IPFS gateway and therefore to our static site. Our last step is to create the certificates for our gateway using cerbot and to adapt our nginx configuration and our hugo configuration to allow dynamic links to access the different pages of our site.

Installation

We will install cerbot once.

apt-get install software-properties-common python-software-properties
add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install python-certbot-apache

Note

It is possible that python-certbot-apache is not working and that we need to use the python3-certbot-nginx package.

After verifying that certbot is working, we can create the certificates for our domain names.

$ certbot --version
certbot 1.21.0

Request a certificate

We just need to do the following command for our nginx server.

sudo certbot --nginx -d example.com -d www.example.com 

If the certificates are created correctly, you can see them in /etc/letsencrypt/live/example.com/.

It is now possible to access our site in HTTPS, you can check with the same system as for the nginx server.

https://<your-ip>/ipns/<key>`

Update Nginx configuration

We will now do some additional configuration to redirect directly to our IPNS link and at the same time to HTTPS.

You can edit your /etc/nginx/workable-sites/ipfs-website file as follows:

server {
    server_name example.com www.example.com ;


    if ($host = example.com) {
        return 301 https://www.watyno.onsager.net$request_uri ;
    }
    location /ipfs {
        proxy_pass http://localhost:8080 ;
        proxy_set_header Host $host ;
        proxy_cache_bypass $http_upgrade ;
        allow all ;
    }

    location /ipns {
        proxy_pass http://localhost:8080 ;
        proxy_set_header Host $host ;
        proxy_cache_bypass $http_upgrade ;
        allow all ;
    }

    listen [: :]:443 ssl ipv6only=on ; # managed by Certbot
    listen 443 ssl ; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem ;
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem ;
    include /etc/letsencrypt/options-ssl-nginx.conf ;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem ;
    
   root /ipns/<key>/ ;
   location / {
        return 301 https://www.example.com/ipns/<key>$request_uri ;
    }

}

server {
    listen 80 ;
    listen [::]:80 ;

    server_name _ ;
    return 301 https://www.watyno.onsager.net$request_uri ;
}

We can check the integrity of our file again and restart the configuration.

sudo nginx -t
sudo systemctl reload nginx

Relative url with Hugo

It can happen that you have a bad redirection when you change page on your site. For this, one of the solutions is to put relativeURLs=true in your config.toml.

Once this change is made, you can rebuild your site and provide your IPNS key.

rm -rf public ## to have no cache
hugo -D
ipfs add -r public
ipfs name publish ${CID}

Conclusion

Congratulations, your static site is now provided and stored on IPFS, by your node. Feel free to add configuration in your config.toml, in the headers of your posts or on your nginx server!

Bonus

Work in progress