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. Updatearchetypes/default
withdraft: false
to make the pages visible. The result of our site is in thepublic
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
DNSlink
Work in progress