~/blog/"How to host your own website on the darkweb"


In my previous post, I explained how to set up a Raspberry Pi on your home network to act as an I2P router, allowing you to browse anonymous sites (and contribute to the network at the same time).

If you’re not familiar with I2P, or how to set up an I2P router. Please checkout my previous post, as this one starts where the other one left off.

Why bother hosting a website on I2P?

I could give you the whole spiel about the importance of censorship resistant systems, freedom of information, decentralisation of the web, etc. But the truth is: I did it because I was bored, and this was fun. So, why not?

You certainly won’t get as much visibility when compared with a website on the clearnet. But you can always have a copy of your personal website on both the clearnet and I2P. However, be aware that speed that you get on I2P is really slow, so keep it light. Who needs anything more than HTML and CSS anyway?

This is the solution that I found to host it. Do let me know if you think it can be improved!

Step 1 - Make a website and host it on your Raspberry Pi

The first step is to make a website. Log into the Raspberry Pi, and create something simple.

cd ~/
echo "<h1>Hello World!</h1>" >> index.html

Now, you need to make it visible to anyone on your home network. For this part, you’ll want to have both docker and docker-compose installed. Also, make sure that you follow the post-installation steps as well, so that you don’t have to run docker with sudo, and that docker starts when the Raspberry Pi boots up.

To host the website, you’ll want to use a webserver like nginx . But to make things easier, you can use it inside a docker container.

Start by creating a directory inside your home called nginx and go into it (you can give it another name, as long as you keep things consistent).

mkdir ~/nginx
cd ~/nginx

Then, create a directory to keep the website in:

mkdir website
mv ~/index.html website/

Now that’s in place, you want to create a docker container running nginx, and tell it where to find your website.

You could just use docker run for this, but docker-compose also allows you to run multi-container applications. One could argue this is a bit overkill, but there is one little feature which I quite like.

Create a new file named docker-compose.yml inside ~/nginx, and paste the following inside:

version: '3'
    image: 'nginx:latest'
    restart: always
      - '80:80'
      - ~/nginx/website:/usr/share/nginx/html
    image: containrrr/watchtower
    restart: always
      - /var/run/docker.sock:/var/run/docker.sock

This will tell docker-compose to launch a multi-container application with 2 containers. The first one (app), will be a webserver with the following characteristics:

That last point is important. It means that whatever you put inside ~/nginx/website/ will be read by the docker container running nginx, and will be served as your website. Therefore, if you want to update it, you just need to change the files inside ~/nginx/website/.

OK that’s pretty obvious, but what’s watchtower?

This is neat little solution that automatically updates app every time a new version of nginx:latest is pushed to Docker Hub. In other words, if a newer version is available, watchtower pulls the latest version, shuts-down the currently running container (i.e. app), and launches a new one running the latest version. Have a look at the documentation for more details.

Once the docker-compose.yml file is saved, all you have to do is launch it:

cd ~/nginx/
docker compose up -d

You can now open a browser from another machine on your network, and give it the internal IP address of the Raspberry Pi.

firefox <raspberry-pi-internal-ip-address>

You should be able to see your website. If this does not work, something went wrong with the webserver. Try to pinpoint the problem before proceeding.

Wait, how do I find this address?

From your Raspberry Pi, you can run the command below to get the address of the interfaces (e.g. if the Raspberry Pi is using the wifi, look at the inet field under the wlan0 section):

ifconfig -a

If you want to find it from the machine where you’re opening the browser, you can scan your network to find it:

sudo nmap -sn  

I won’t go into detail on how nmap works, as it’s outside of the scope of this tutorial.

Step 2 - Make the website accessible to the rest of the I2P network

As mentioned in the introduction, at this point, you should have i2pd configured in the same way as described in my previous post. I’m assuming that’s the case for you.

Now that you can see the site from a different machine on your network, it’s time to make it visible on i2p. For that, you’re going to need to change the tunnels.conf file. It’s usually on ~/.i2pd/tunnels.conf , but that might not be the case for you.

If you access the i2pd webconsole (, you should see a Data path field on the main page (in my case, it says /var/lib/i2pd). That’s the directory where you can find the tunnels.conf file. Open that file (you might need to use sudo), and add the following to the bottom:

type = http
host =
port = 80
keys = anon-website.dat

Restart i2pd:

sudo systemctl restart i2pd.service

Open the webconsole, and go to the tunnels page (, you should see a subheading saying anon-website followed by a long string with *.b32.i2p:80 at the end. That’s the address to the website (you should be able to open it in the browser after a while).

Step 3 (Optional) - Get a human-readable address for your fancy new website

Congratulations! You are now hosting your own website on the darkweb.

But that long string is rather ugly, wouldn’t it be nice to have a nice human-readable address?

To do that, go into the webconsole > I2P tunnels > anon-website. On this page, click on “Address registration line” and fill out which domain you’d like. If the generation is successful, it should take you to another page, asking to add a small description. Submit it, and wait a bit until the new address works (it could be a couple of days, so don’t stand there refreshing in despair).

You shouldn’t need anything else. But if at any point you need to generate an authentication string, you’ll need to use i2pd-tools, in particular readdr:

./regaddr anon-website.dat <your-domain>.i2p > auth_string.txt
cat auth_string.txt

The anon-website.dat file can be found in the same directory as the tunnels.conf file. Follow the instructions on the project’s repo on how to compile i2pd-tools.

After a few days, you should be able to see the website being announced on planet.i2p and identiguy.i2p.

And that’s it! Enjoy your own piece of virtual real estate on the darkweb.