Posted on

Table of Contents

This guide was originally written 2020 April 23 but updated 2023 August 7


This guide ignores the default torrc and sets up two new onions dedicated to their purpose. Do this so you're not exposing the SSH daemon or your public SSH key via your public onion address. Duplicate web* instance steps if you're going to use Onionbalance, and duplicate ssh* instance steps if you want backup circuits to get back into your Pi.

This guide also presumes certain things. This server is behind NAT and does not have a public IP. While you could use this guide to setup a remote virtual machine (I would never advise this unless you own the harware), I set up this server with a USB keyboard that I have direct access to.

The first step is to block everything inbound. Be careful with this if you are setting up a remote system. If and when I have a public IP, I like to deny everything inbound first so that bots run by Eve cannot grab my public SSH key as soon as I make a cleartext request to install Tor.

Imagine a passive or active adversary with network visibility. This includes your ISP, maybe your government, or maybe well-funded global passive adversaries. They might create an automatic system to track the activity and behavior of any IP that initiates a clear-text (plaintext or tls-encrypted cleartext (metadata)) install of tor. Imagine that system adding your IP to a surveillance list that then automates monitoring the uptime of your system and juxtaposes that behavior to a seprate system that tracks the bahavior of known onion sites in attempts to identify the physical location of onion sites and services. Metadata privacy matters. Do not expose port 22 to the internet.

Another presumption is that you do not need php, sql, or other heavy and vulnerable code, and is why I use nginx-light. Further, I do not bother with TLS and adding another potentially-vulernable dependency like openssl. Tor onion access is end-to-end encrypted by default.


Testing has been updated to Debian 12.

block everything inbound

su root

apt update && apt install ufw gpg vim -y

ufw enable

install tor

Directions simplified based on

vim /etc/apt/sources.list

Add this line:

deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] bookworm main

Install Tor Project's cert:

wget -qO- | gpg --dearmor | tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null

Install tor:

apt update && apt install tor -y

create onion site

By default, sbin directories aren't accessible in our path:

echo 'export PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin' >> ~/.bashrc

source ~/.bashrc

Create a new, isolated tor instance named web1:

tor-instance-create web1

vim /etc/tor/instances/web1/torrc

Delete everything in this torrc file and and use:

HiddenServiceDir /var/lib/tor-instances/web1/hidden_service/
HiddenServicePort 80

Restart the new service:

service tor@web1 restart

View the new port 80 (web) onion address:

cat /var/lib/tor-instances/web1/hidden_service/hostname

Output will look like:


create ssh onion

tor-instance-create ssh1

vim /etc/tor/instances/ssh1/torrc

Delete everything and use:

HiddenServiceDir /var/lib/tor-instances/ssh1/hidden_service/
HiddenServicePort 22

sudo service tor@ssh1 restart

sudo cat /var/lib/tor-instances/ssh1/hidden_service/hostname


install web server

sudo apt install nginx-light

Edit the default nginx site file:

sudo vim /etc/nginx/sites-available/default

Change the default server to:

listen default_server;

Restart nginx:

sudo service nginx restart

ssh and scp from macOS client via tor

Install tor and torsocks

brew install tor torsocks

Edit ssh (client) config:

sudo vim /etc/ssh/ssh_config


UseRoaming no
proxyCommand nc -x %h %p

Copy your ssh pub key to the server via tor onion:

ssh-copy-id user@zyxwvutsrqponmlkjihgfedcba.onion

ssh to the onion server:

ssh user@zyxwvutsrqponmlkjihgfedcba.onion

scp site data to the onion's web server folder:

scp -r ./_site/* user@zyxwvutsrqponmlkjihgfedcba.onion:/var/www/html/.

If you like this content, consider sending me an anonymous tip with Zcash: zs195rfh80s5m6chxrqej57fg9vxw2ypw2p9ppv3e5f44dstcu36f59pxfukugmgzxnp2djvu6w2jd