7 min read

My first NFT with ENS and IPFS

My first NFT with ENS and IPFS

Ok, so this isn't my first NFT, but the current title hits a lot harder than:

My first NFT that isn't an overpriced JPEG or playing card from an online game

Remaining up-to-date with technology as it evolves, albeit to a reduced depth concordant with my gradual tech withdrawal, is something I've blogged about previously.

I've had my eye on the Ethereum Name Service (ENS) for a while; especially since I'm an accidental domain collector for side projects that I complete 80% of.

My interest in the ENS rose even further over the last couple of months after seeing a lot more people from crypto Twitter change their name to <username>.eth.

This weekend, with New Year out of the way I decided to register my own domain, and stencil ownership of it immutably into the blockchain.

After doing some cursory research into the steps I'd need to take, I jotted down my next steps so I'd have a rough plan to work to:

  1. Set up an IPFS node to host my ETH website
  2. Add the ETH website content to my IPFS node so it could be distributed
  3. Register the domain
  4. Associate the content hash of my website with the domain

Why a domain?

The choice to purchase a domain NFT rather than a JPEG like everyone else was also pretty clear to me. While there's a lot of hype (and a lot of money changing hands) in the profile picture space, I'm treating the majority of picture collections as proofs of concept.

The real benefit of an NFT comes from provable and irrevocable ownership of something and a domain is an example of something I would use rather than just hodl. Whilst I think we're still early in the crypto hype cycle, and NFTs are pumping right now, there's a lot both to come and to stabilise.

Hype Cycle of Blockchain 2021 https://blogs.gartner.com/avivah-litan/2021/07/14/hype-cycle-for-blockchain-2021-more-action-than-hype/

Over the next few years, there'll likely be more concrete use cases for what today seems like a fad. I'm personally very interested in zero-knowledge proofs as that will completely change the way authentication is completed – say goodbye passwords!



The reason I decided to host my own IPFS node was twofold:

  1. I didn't want to pay a third party
  2. I wanted to learn how to do it so I'd be able to talk about it

It was startlingly easy to set up IPFS on one of my servers by including andrewrothstein's ipfs role and then tweaking a custom role of my own to run the service in the background as a daemon.

- name: Add systemd service for ipfs daemon.
    src: ipfs.service
    dest: /etc/systemd/system/ipfs.service
    owner: root
    group: root
    mode: '0644'

- name: Make sure ipfs service is running
    name: ipfs
    state: started
    enabled: yes
main.yml for my ipfs role
Description=IPFS Daemon
After=syslog.target network.target remote-fs.target nss-lookup.target

ExecStart=/usr/local/bin/ipfs daemon --enable-namesys-pubsub --enable-gc --init --init-profile=server --routing=dhtclient

ipfs.service definition file

As an optional addition, I decided to be a good netizen by opening up port 4001 so my node would play a more active role in the network and so in the event that no other nodes pin my content, it can always be found.

Whilst I didn't do this for speed, another good recommendation would be to create a service user called ipfs with a nologin shell.


Creating and pinning the content was also super easy. While I could have created an entire website to be hosted in ipfs, I surmised that the most valuable thing to do would be to create a redirect to this site.

The HTML I wrote was extremely basic, and in hindsight should probably have used a meta refresh tag rather than JavaScript to be more inclusive to noscript users.

<!DOCTYPE html>
<html lang="en" dir="ltr">
        <meta charset="utf-8">

            window.location.href = "https://www.adammalone.net"
The index.html file which would become my ETH website.

Taking this file, I added it to ipfs and pinned so it would persist and prevent it from being garbage collected.

adam@ipfs:~$ ipfs add ~/ipfs/index.html
 added QmRw4UV4UukUydbKXshFD9UwghpWZeFYd4Nsrp4UChSSEh index.html
 243 B / 243 B [===========================] 100.00%
adam@ipfs:~$ ipfs pin add /ipfs/QmRw4UV4UukUydbKXshFD9UwghpWZeFYd4Nsrp4UChSSEh
pinned QmRw4UV4UukUydbKXshFD9UwghpWZeFYd4Nsrp4UChSSEh recursively
Adding and pinning my file.


In order to test that everything worked as expected, I wanted to use the web UI to check my commands had the desired outcome.

To do this without opening UI ports to the internet, I created an SSH tunnel and forwarded my local ports to the remote ipfs node.

ssh -L 5001: ipfs.example.com
Command required to tunnel local port 5001 to remote port 5001.

The SSH tunnel allows me to connect to port 5001 on my local laptop and have that securely forwarded through the tunnel to port 5001 on my remote server. So when my browser navigates to, it's actually from the frame of reference of the remote server.

How SSH tunneling works.

From there, I was able to open a browser on my local laptop and navigate to to confirm the daemon was working and confirm that my pinned files existed and the content matched.

When using the IPFS explorer, sometimes pinned files aren't shown in the files tab. To show them, manually alter the URL from .../#/files to .../#/pins


I was curious about how a file on my node could be located and accessed by other people using only a content identified (CID) and it turns out I'd used something similar previously.

Over in my blog titled How I got into technology, I discussed my use of the Direct Connect (DC) protocol. It turns out both DC and IPFS use Distributed Hash Tables (DHT) as the mechanism of publishing to the world who has what content (and how to get there).

Turns out DHT is pretty cool.

Any content you make available on IPFS gets cryptographically hashed and assigned an associated content ID (CID). That means:

  • Any difference in the content will produce a different CID and
  • The same content added to two different IPFS nodes using the same settings will produce the same CID.

In my case, the cryptographic hash for my file is QmRw4UV4UukUydbKXshFD9UwghpWZeFYd4Nsrp4UChSSEh

As with DC, peers are connected together within the IPFS network. The lookup algorithm connects to the 10 closest peers and asks who their closest peers are to the CID we're looking for. Traversing the list of peers and eventually finding a peer that has the content.

This of course looks super cool when visualised. From my home node, I queried the hash of the file I've stored in my public IPFS node and used Protocol Labs' code to chart the queries.

A visualisation of how a CID is located using DHT

A few challenges

I found that after running go-ipfs on my node for a couple of weeks, I noticed that my server resources were being expended almost entirely by the daemon. I could have tried was the nice sledgehammer approach, but a little bit more research indicated that setting systemd configuration options may also work. By using --init-profile=server --routing=dhtclient we instruct the IPFS daemon to disable local host discovery and use DHT in client only mode.

I was tempted to change the init profile to lowpower, but the server resource issues cleared up with the above options.


Registering my domain

Once I'd created the payload, it was easy enough to register on ens.domains and sign the transaction to take ownership of the DNS address I'd requested. The annoying majority of the price however was gas fees, and another reason why Layer 2 Rollups (L2s) are something I'm learning more are a way to reduce adoption blockers.

I opted to purchase my domain for 10 years at a cost of 0.002Ξ per year (plus gas).

Linking domain & website

After ENS had detected my payment for the domain and the token was attached to my wallet, I executed a different function on the smart contract to change the content of the token to point to my CID. This can now be observed on etherscan along with any other metadata I decide to include.

This now means I have of course joined all the other crypto sheep and changed my Twitter name to adammalone.eth.

For the future

As was discussed in my Ghost on Tor blog, this website is now available on the tor network. In addition to tor, it's now (sort of) available on IPFS.

While a user can navigate to adammalone.eth (or adammalone.eth.link if your browser isn't IPFS enabled), the actual IPFS part is merely a redirect to the .net domain.

For me to run the website entirely on IPFS, I'll need to start looking more into IPNS as this will allow me to update the website and content without continually having to update the CID within my ENS token.