Skip to content

Run Multiple Onion Services on One Host

Created September 12, 2026 • Last updated September 12, 2026 • 5 min read • 231 views

Joshua

HackIndex Creator

One Tor daemon can serve multiple onion services at once, each with its own address. This page runs several onions from the hardened onion service build, maps each to its own site, and locks one address to a key with client authorization. Every service adds its own directory and its own backend port.

Running multiple onion services from one daemon

Each service is a HiddenServiceDir block in torrc. Every option after a HiddenServiceDir line applies to that service until the next HiddenServiceDir line, so order matters. Point each service at a different loopback port, and let nginx answer that port with its own site.

One daemon, several addresses

Add a block per service. The first is the site from the base build on port 8080. The second serves a different site on port 8081. Each block gets its own address and key. Repeat the intro DoS and proof of work lines from the base build under each block, since those settings are per service.

HiddenServiceDir /var/lib/tor/site/
HiddenServicePort 80 127.0.0.1:8080

HiddenServiceDir /var/lib/tor/blog/
HiddenServicePort 80 127.0.0.1:8081

Give the second service its own nginx server block on the matching port. Apply the same hardening as the base build: version banner off, directory listing off, and the security headers inside the block.

server {
    listen 127.0.0.1:8081;
    server_name _;

    root /var/www/blog;
    index index.php index.html;

    server_tokens off;
    absolute_redirect off;
    port_in_redirect off;
    autoindex off;
    access_log off;
    etag off;
}

The same site on several addresses

To publish one site under more than one address, point another service block at the same backend port. This helps when you rotate an address or migrate visitors from an old one. Both onions serve the exact same content. Add only the new block under the existing site block. Do not repeat the site directory, or Tor refuses to start with a duplicate directory warning.

HiddenServiceDir /var/lib/tor/site-mirror/
HiddenServicePort 80 127.0.0.1:8080

A private admin onion with client authorization

Client authorization restricts a service to clients that hold a key. Once you enable it, the address alone no longer grants access, which suits an admin panel. Add the service like any other, then place a client key in its authorized_clients directory.

HiddenServiceDir /var/lib/tor/admin/
HiddenServicePort 80 127.0.0.1:8082

Restart Tor once so it creates the admin service and its address. Then generate an x25519 keypair. The public key goes to the service, the private key to the client. Generate it offline and move the keys by hand. The base32 tool comes from GNU coreutils, so on macOS run brew install coreutils and use gbase32, or generate the keys on the Linux server itself.

user@host ~ $ openssl genpkey -algorithm x25519 -out /tmp/k1.prv.pem
user@host ~ $ openssl pkey -in /tmp/k1.prv.pem -outform DER | tail -c 32 | base32 | sed 's/=//g' > /tmp/k1.prv.key
user@host ~ $ openssl pkey -in /tmp/k1.prv.pem -pubout -outform DER | tail -c 32 | base32 | sed 's/=//g' > /tmp/k1.pub.key

Write the public key into the service as a .auth file. The format is descriptor:x25519: followed by the key, one line only. Fix the owner and restart Tor to activate the restriction.

user@host ~ $ sudo mkdir -p /var/lib/tor/admin/authorized_clients
user@host ~ $ echo "descriptor:x25519:$(cat /tmp/k1.pub.key)" | sudo tee /var/lib/tor/admin/authorized_clients/client.auth
user@host ~ $ sudo chown -R debian-tor:debian-tor /var/lib/tor/admin
user@host ~ $ sudo systemctl restart tor

With the key in place, the service rejects anyone without it. Confirm this from a client that has no key yet. Set ONION_ADDRESS to the admin hostname from /var/lib/tor/admin/hostname on the server. Tor Browser shows an Onionsite requires authentication prompt instead.

user@host ~ $ ONION_ADDRESS=<paste the admin hostname from the server>
user@host ~ $ torsocks curl -I http://$ONION_ADDRESS/
torsocks: General SOCKS server failure (in socks5_recv_connect_reply() at socks5.c:527)

On the client, point Tor at a directory for private auth keys, then drop the private key there. The file name ends in .auth_private, and the line starts with the admin address without the .onion suffix.

ClientOnionAuthDir /var/lib/tor/onion_auth
user@host ~ $ ONION_ADDRESS=<paste the admin hostname from the server>
user@host ~ $ sudo mkdir -p /var/lib/tor/onion_auth
user@host ~ $ echo "${ONION_ADDRESS%.onion}:descriptor:x25519:$(cat /tmp/k1.prv.key)" | sudo tee /var/lib/tor/onion_auth/admin.auth_private
user@host ~ $ sudo chown -R debian-tor:debian-tor /var/lib/tor/onion_auth
user@host ~ $ sudo chmod 700 /var/lib/tor/onion_auth
user@host ~ $ sudo systemctl restart tor

Scaling beyond one host

One daemon on one box has a ceiling. To spread an address across several backend instances for load or redundancy, Onionbalance publishes a combined descriptor that points at multiple instances. That sits outside this build, but it is the next step when a single host is not enough.

Give any of these addresses a readable prefix with mkp224o, and revisit the full lockdown in the hardened onion service guide.

References