The Decentralized Web: Develop and Publish a Website

In the last part, we will get our hands dirty and play with IPFS to publish a website. If you want to dig deeper into IPFS later, please have a look at the official documentation hosted on IPFS.

You can use IPFS Desktop and the IPFS Daemon with the same data, but not run them at the same time. You need to stop the one to use the other. But you can use the IPFS commands to interact with a running IPFS Desktop service.


To install IPFS Desktop, you download the binary for your operating system (Mac, Windows, or Linux/FreeBSD).

If you want to use Brave, you can navigate to the browser settings and activate in the IPFS section the IPFS Companion. You can click on the My Node button to open the Web UI. You can change the IPFS Node type in the settings of the companion to use the external node from the IPFS Desktop installation. I haven’t figured out yet if it’s possible to use the command-line tool to access the native Brave IPFS node.

To follow along with the tutorial, we use the command-line tool. You can install IPFS via Homebrew on a Mac:

$ brew install ipfs

For other options, like the M1 install, please look at the installation instructions.


If you installed IPFS Desktop, you’ll see the Web UI inside a window which can be accessed through the app itself. You can open it through the My Node button of the companion extension, or by using the URL shown to you when you start the command-line daemon.

The interface has navigation with multiple items: Status, Files, Explore, Peers, and Settings.

Status is a monitor of incoming and outgoing traffic of your IPFS node. Files show all your hosted and pinned files and folders. Explore is an advanced tool to explore hashes. Peers shows a world map with nearby peers. Settings allows configuring language, public gateway, API address, and other things. The CLI-Tutor-Mode is a useful thing for beginners. It shows next to each command in the graphical interface the accompanied terminal command.

Initializing the Repository

If you didn’t install the IPFS Desktop, you’ll need to initialize the IPFS repository before the first start:

$ ipfs init

Exploring Files

You can explore objects in your repository with the ipfs cat command, for example:

$ ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme

Or contents of folders with the ipfs ls command:

$ ipfs ls /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/

Start the Node

To run the node on the command line, you have to start the daemon:

$ ipfs daemon

If the IPFS Desktop app is running, you’ll get an error that tells you, another process is already using the repository:

Error: lock /Users/username/.ipfs/repo.lock: someone else has the lock

Add your First File

Create your first file and add it to the repository:

$ echo "Hello World" > hello-world.txt
$ ipfs add hello-world.txt

With the same ipfs cat command from above, but using the hash of the hello-world.txt file, you can see its contents.

Create a Simple Webpage

We will create now a simple webpage:

$ cd ~
$ mkdir simple-webpage
$ cd simple-webpage

We download an image of a cute cat from IPFS:

$ ipfs cat QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg > cat.jpg

Create a new file named index.html inside the folder and paste this basic HTML inside:

<!DOCTYPE html>
    <title>Nice Kitty</title>
      <h1>Nice Kitty</h1>
      <img src="cat.jpg" />

We’ll now add this folder and all its content (-r for recursively) to IPFS:

$ cd ..
$ ipfs add -r simple-webpage/

You’ll see an output like this, but with different hashes:

added Qmd286K6pohQcTKYqnS1YhWrCiS4gz7Xi34sdwMe9USZ7u simple-webpage/cat.jpg
added QmY3ayxXcXMs7qdDvPy7QKcVQ492JBNyC9Zf3jp1LrQRCA simple-webpage/index.html
added QmNa1YdawLNeD2fumihKGxuna4GqAws2QsvRsMjzpY2mM2 simple-webpage
 433.02 KiB / 433.02 KiB [==========================================] 100.00%

You can see your website now by opening the following URL in Brave:<website-hash>

It should work in other browsers because this URL is using a gateway, but it might be slow or take a while until the file is available.

In Brave, you’ll see a button in your URL bar to open it directly in IPFS (without a gateway). This will change the bar to show an IPFS icon and the IPFS address.

Publishing to IPNS

Each time you change something with your website, you’ll get new hashes for the changed files and folders affected, which makes it hard to always serve your recent version to the world. This is where IPNS (the InterPlanetary Name System) comes into play. You create an IPNS hash that is tied to your Peer ID.

We publish now our website to IPNS:

$ ipfs name publish <website-hash>

You’ll get an output like this:

Published to <your-peer-id>: /ipfs/<website-hash>

This peer ID is now available by accessing it in your browser:<your-peer-id>

In Brave, you can directly access it with the ipns:// protocol (it might take a few seconds, until it’s available):


You can check where the IPNS is pointing by using this command:

$ ipfs name resolve <your-peer-id>

If you change your website, add it again to the IPFS repository, and publish it again, you’ll see the updated content at that address.

Use a New IPNS Name Key Pair

To publish multiple projects at fixed URLs, you’ll need to generate a new key pair:

$ ipfs key gen <some-name>

You can see your keys with this command:

$ ipfs key list

You can publish to a different IPNS name by adding another key pair to the publish command:

$ ipfs name publish --key=<some-name> <website-hash>

MFS – Mutable File System

If you open the IPFS Web UI and navigate to the Files section, you might wonder why it’s empty.

The reason is that files in IPFS are content-addressed and immutable. You can’t overwrite them, create a new version. The hashes in your repository get additionally cleaned up automatically (unless they are pinned) when the cache is full, or you run the cleaning task (garbage collection) manually:

$ ipfs repo gc

If you add a file with ipfs add it will automatically be pinned.

MFS helps to make working with files more comfortable. We can now copy our website to the MFS:

$ ipfs files cp /ipfs/<website-hash> /simple-website

This will create a new folder at the root of the Mutable File System and copy the contents of the website to it. If you reload the Files section of the IPFS Web UI, you’ll see the folder, see its contents, and hashes. You would use MFS for projects you plan to keep track of and have them stay around longer.

You can use a row of UNIX-like commands to create a folder, write files, and copy and move files on MFS. Furthermore, you can find all commands starting with ipfs files in the Command-line reference. This command would create a new file inside a folder:

echo "Hello, World" | ipfs files write --create --parents /my-new-folder/hello-world.txt


If you share content on a decentralized, distributed network, it will be available, if at least one node serves it. If you’re the only node on the network and turn off your computer, the data won’t be accessible.

Pinning services solve this problem for you, they allow pinning CID’s to keep them online. I tried Pinata because they offer 1 GB of storage for free.

Hosting IPFS Content With a Domain

There are plenty of different options to serve content with a human-readable name. You could register an ENS name, and connect it to your latest CID, buy a domain at Unstoppable Domains, or use a service like Fleek, to name a few options.

I use Fleek because I just wanted to play around with IPFS, and they offer free hosting. I connected the GitHub repository of my website with Fleek and automatically deploy my website on each push to IPFS. Not only that, but I deploy it currently to a subdomain by adding a CNAME record in my DNS settings, but have bought an Unstoppable Domain.


I’m happy with how the decentralized internet is progressing and even though it feels bumpy and like the early days of the internet. A lot of tinkering, and trying out new ideas and technologies, but it is this feeling that makes me hopeful that we witness the beginning of something new.

I hope we will be able to bring back the control of the internet to the masses and give control of data to the individual.

We need to stop malicious and greedy big tech oligarchs and authoritarian politicians from infringing on our rights to life, liberty, and the pursuit of happiness.