Installing Secure Tor Onion Hidden Services on Ubuntu (CLI and Webmin Guide)
Complete Guide: Installing Secure Tor Onion Hidden Services on Ubuntu Server or Laptop
Part 1 will cover how to manually install and configure multiple Tor Onion services on the latest Ubuntu versions using CLI, including setting up NGINX, PHP, and MySQL/MariaDB, configuring multiple hidden services, securely uploading site files, creating databases, hardening the server, and managing the firewall and open ports.
Part 2 will demonstrate how to do the same tasks using the Webmin panel, including installation, access setup (both via LAN and Tor Onion address), NGINX/PHP/MariaDB integration, virtual host management for multiple onion sites, file uploads, database administration, security tweaks like port changes, and mapping the Webmin panel to a .onion address.
Installing Tor Onion Hidden Services on Ubuntu (CLI and Webmin Guide)
Tor “hidden services” (onion services) let you host websites with .onion
addresses for anonymous access. We’ll cover Part 1: manual CLI setup and Part 2: using the Webmin web panel on Ubuntu (e.g. 22.04 LTS). The stack includes Nginx, PHP, and MySQL/MariaDB for both static and dynamic (WordPress, PHP) sites. We use all commands via terminal (SSH). Finally, we’ll harden the server (firewall, fail2ban, disable root login, etc.) and even map Webmin to its own onion address.
Tor onion hidden services route traffic internally, so the webserver only listens on localhost. This keeps your server IP hidden and restricts access to Tor only. Onion addresses are 56-character .onion
URLs generated by Tor. To set one up, first ensure Ubuntu is updated and a non-root sudo user exists. For example, create a sudo user (replace alice
with your name):
sudo adduser alice # create new user
sudo usermod -aG sudo alice # add user to sudo group
This follows Ubuntu’s recommended initial setup steps. You should also disable root SSH logins in /etc/ssh/sshd_config
(set PermitRootLogin no
and PasswordAuthentication no
), then restart SSH. This forces all admin work through your sudo user.
Part 1: CLI Installation and Configuration
1. Update System and Basic Security
Update packages and install essential tools:
sudo apt update && sudo apt upgrade -y
sudo apt install curl gnupg2 ufw fail2ban -y
Set up a basic firewall (UFW) to allow SSH and later Nginx or other services. For example, allow OpenSSH and enable UFW:
sudo ufw allow OpenSSH
sudo ufw enable
Check status to confirm SSH is allowed. By default UFW now denies other incoming connections. Later, you may allow web ports as needed.
Install fail2ban to block brute-force attacks on SSH or web forms:
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban
Fail2ban will ban IPs that fail login too many times, adding extra protection.
2. Install Tor and Configure Onion Service
Add the official Tor repository (recommended for up-to-date Tor). For Ubuntu, you can use the Tor Project’s apt repo. Example (for Ubuntu 22.04 “jammy”):
sudo apt install apt-transport-https -y
curl https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | \
gpg --dearmor | sudo tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org $(lsb_release -sc) main" | \
sudo tee /etc/apt/sources.list.d/tor.list
sudo apt update
sudo apt install tor deb.torproject.org-keyring -y
Alternatively on newer Ubuntu, simply:
sudo apt install tor -y
but using the Tor repo ensures the latest stable version. After installation, Tor will run as user debian-tor
.
Edit Tor’s config in /etc/tor/torrc
. At the end of this file, add one or more HiddenService stanzas for each onion site. For example, for a website listening on local port 8080:
HiddenServiceDir /var/lib/tor/my_onion_site/
HiddenServicePort 80 127.0.0.1:8080
This tells Tor to create /var/lib/tor/my_onion_site/
and forward port 80 of the onion to localhost:8080. Each HiddenServiceDir
(with accompanying HiddenServicePort
) produces a separate .onion
address. You can add multiple services by repeating HiddenServiceDir
lines. For example:
HiddenServiceDir /var/lib/tor/site1/
HiddenServicePort 80 127.0.0.1:8080
HiddenServiceDir /var/lib/tor/site2/
HiddenServicePort 80 127.0.0.1:8081
This creates two onions pointing to different ports. Make sure to mkdir /var/lib/tor/site1
and chmod 700
it if needed. After saving torrc
, restart Tor:
sudo systemctl restart tor
Tor will generate a hostname
file in each HiddenServiceDir containing your new .onion
address. For example:
sudo cat /var/lib/tor/my_onion_site/hostname
Save these addresses – they are your site URLs.
3. Install Nginx, PHP, and MySQL/MariaDB (LEMP stack)
Next install the web and database stack. We’ll use Nginx, PHP-FPM, and MariaDB (MySQL-compatible). Update apt and run:
sudo apt update
sudo apt install nginx -y # Install Nginx web server:contentReference[oaicite:11]{index=11}
By default Nginx will listen on port 80. We’ll later configure it to only listen on localhost for anonymity.
Install the database server (MariaDB is a drop-in replacement for MySQL):
sudo apt install mariadb-server mariadb-client -y # or use mysql-server:contentReference[oaicite:12]{index=12}
Secure the database installation by running the built-in script:
sudo mysql_secure_installation
Answer prompts (e.g. set a root password, remove anonymous users, disallow remote root, remove test DB, etc.). This locks down MySQL/MariaDB as recommended.
Install PHP and related modules. For example (Ubuntu 22.04 has PHP 8.1):
sudo apt install php8.1-fpm php8.1-mysql php8.1-cli php8.1-mbstring php8.1-zip -y
This installs php-fpm
and the MySQL extension. The PHP-FPM service (php8.1-fpm
) should start automatically.
Test Nginx: open http://your_server_ip/
in a browser (clearnet) to see the Nginx welcome page. If using UFW (as above), allow HTTP:
sudo ufw allow 'Nginx Full'
or just port 80. Verify with ufw status
.
4. Configure Nginx Virtual Hosts for Onion Sites
For each onion address (HiddenService), configure an Nginx server block. For example, for the site above listening on port 8080, create a new config:
sudo mkdir -p /var/www/my_onion_site
echo "<html><body><h1>It works!</h1></body></html>" | sudo tee /var/www/my_onion_site/index.html
sudo chown -R www-data:www-data /var/www/my_onion_site
sudo chmod -R 755 /var/www/my_onion_site
Then create /etc/nginx/sites-available/my_onion_site
:
server {
listen 127.0.0.1:8080;
server_name YOUR_ONION_ADDRESS.onion;
root /var/www/my_onion_site;
index index.php index.html index.htm;
location / {
# Only allow Tor (localhost) to access the site
allow 127.0.0.1;
deny all;
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
# (Disable any server tokens for privacy)
server_tokens off;
}
Replace YOUR_ONION_ADDRESS.onion
with the address from Tor’s hostname file. Note that Nginx is set to listen 127.0.0.1:8080
, matching the Tor HiddenServicePort. This ensures the site is only accessible via Tor. The allow 127.0.0.1
/deny all
enforces it (only Tor’s requests from localhost will reach it). Similar to examples in guides, we listen on localhost and set server_name
to the onion.
Enable the site and restart Nginx:
sudo ln -s /etc/nginx/sites-available/my_onion_site /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Repeat for additional onion sites (different ports and directories, each with its own HiddenServiceDir in Tor).
5. Upload Site Files and Configure Databases
Upload your website files (HTML/PHP) into /var/www/your_site/
. You can use scp
or rsync
from your local machine, or use Nginx’s text editor. For example:
scp -r ~/mywebsite_files/* alice@server_ip:/var/www/my_onion_site/
Ensure permissions: sudo chown -R www-data:www-data /var/www/my_onion_site
.
For dynamic sites (WordPress, PHP apps), create a MySQL database and user via the terminal:
sudo mysql -u root -p # enter MariaDB root password
Then inside the MySQL shell:
CREATE DATABASE dbname CHARACTER SET utf8mb4;
CREATE USER 'dbuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON dbname.* TO 'dbuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Use dbname
, dbuser
, and a strong password
as needed. WordPress and other scripts can then use these credentials to access the database.
6. Final Security Hardening
Now that services are running, tighten security:
- Firewall: Verify UFW only allows needed ports. For onion-only sites, external HTTP (80) need not be open. Make sure
ufw allow ssh
is enabled. You may also allow SSH on a non-standard port for obscurity (e.g. change SSH port in/etc/ssh/sshd_config
andsudo ufw allow <new_port>
). - Disable Unused Services: Remove or disable any services you don’t need.
- Server Updates: Enable automatic security updates, or regularly run
sudo apt update && sudo apt upgrade
. - Intrusion Prevention: We installed
fail2ban
to ban repeated SSH or web login failures. - SSH Keys: Use SSH keys (disable password login) for your user – DO’s initial setup guide shows how.
- Hide Version Info: We disabled
server_tokens
in Nginx. You can similarly hide PHP and database version details. - Port Knocking / Security through Obscurity: Optionally, use non-default ports (e.g. SSH on 2222). Remember to reflect these in UFW (e.g.
ufw allow 2222/tcp
). - Tor-specific: Only listening on localhost (127.0.0.1) prevents any IP leaks. Tor’s official docs recommend using Unix sockets or localhost binding. Follow their advice on not revealing server info.
At this point your onion sites should be accessible via the Tor Browser at http://your_onion_address.onion
. They will be isolated from the clearnet. SSH remains your secure access to the server.
Part 2: Installation and Setup with Webmin
Webmin provides a browser interface to manage your server. We’ll install Webmin and then repeat the above tasks through its UI.
1. Install Webmin on Ubuntu
Add the Webmin repository and install Webmin (Ubuntu 22.04 example):
curl -fsSL https://download.webmin.com/jcameron-key.asc | sudo gpg --dearmor -o /usr/share/keyrings/webmin.gpg
echo "deb [signed-by=/usr/share/keyrings/webmin.gpg] http://download.webmin.com/download/repository sarge contrib" | sudo tee /etc/apt/sources.list.d/webmin.list
sudo apt update
sudo apt install webmin -y # install Webmin package:contentReference[oaicite:23]{index=23}
By default, Webmin listens on port 10000 and uses HTTPS. After installation it will start automatically. Allow its port through the firewall:
sudo ufw allow 10000/tcp # allow Webmin GUI:contentReference[oaicite:24]{index=24}
You can now access Webmin in a web browser at https://SERVER_IP:10000
(accept the self-signed certificate warning). Log in as your sudo user (or root if enabled). You may wish to set a strong password or SSH key auth.
To access Webmin via Tor, add another hidden service for port 10000. In /etc/tor/torrc
, append:
HiddenServiceDir /var/lib/tor/webmin_service/
HiddenServicePort 10000 127.0.0.1:10000
Restart Tor. Tor will create /var/lib/tor/webmin_service/hostname
containing a new .onion
address for Webmin. You can connect to this via Tor Browser to manage your server anonymously.
2. Using Webmin to Install Nginx, PHP, and MariaDB
Webmin’s “System” ➔ “Software Packages” module lets you install packages without CLI. However, many prefer the Terminal module in Webmin for commands. You can also do under “System” ➔ “Software Package Updates”. Install Nginx, PHP, and MariaDB through Webmin’s APT package manager: search for nginx
, php8.1-fpm
, php-mysql
, and mariadb-server
and install them. This is equivalent to the CLI commands above (e.g. sudo apt install nginx
, etc.).
Alternatively, if you already did the CLI install above, simply configure them via Webmin. Ensure Nginx and PHP-FPM are running (check “System” ➔ “Bootup and Shutdown” to confirm services).
3. Adding Onion Sites in Webmin (Nginx Virtual Hosts)
In Webmin’s sidebar under “Servers” ➔ “Nginx Webserver”, you can manage virtual hosts. Create a new Virtual Server (or “Server Block”) for each onion site. Set:
- Listen:
127.0.0.1:<port>
(e.g. 8080). - Server Name: use the
.onion
hostname. - Document Root: e.g.
/var/www/my_onion_site
. - Other settings: Configure index files and enable PHP processing by selecting
fastcgi_php
in Webmin. Under “Edit Directives”, ensure you havelocation
blocks similar to the CLI config above (allow 127.0.0.1; deny all;
).
Webmin can generate the server { }
block for you. After saving, Webmin will create the config file and reload Nginx. This replicates the manual Nginx setup.
4. Uploading Files and Managing Databases in Webmin
Webmin has a “File Manager” (under Tools) to upload or edit site files directly. Navigate to the document root (e.g. /var/www/my_onion_site
) and upload your site files (HTML, PHP, etc.). You can also use SFTP via your SSH user if preferred.
For databases, use “Servers” ➔ “MySQL Database Server”. Click “Create new database”, and “Create new user”, mirroring the CLI SQL steps. You can also import SQL dumps here. Webmin makes it GUI-easy to configure MySQL/MariaDB without touching the command line.
5. Security and Port Customization
You can tweak ports and security in Webmin as well. In “Networking” ➔ “Port Configuration”, you could change SSH’s port (if installed) for obscurity (remember to update UFW). For Webmin itself, you could edit “Webmin Configuration” ➔ “Ports and Addresses” to change 10000 to a custom port.
To allow Webmin over Tor only, you could also restrict Webmin’s firewall rule to Tor’s user (127.0.0.1). For example, UFW can be set to allow from 127.0.0.1 to any port 10000
instead of open SSH. This ensures Webmin isn’t reachable over the internet directly – only via Tor. The Tor forum suggests firewall rules like ufw allow from 127.0.0.1 to any port 10000
to lock services to local only.
Finally, ensure Webmin itself is secured (strong password, up-to-date Webmin version). You can also install the Fail2Ban Webmin module to manage bans from the UI.
Summary
By following these steps, you will have a production-ready Ubuntu server (or VM) hosting one or more .onion websites via Nginx, PHP, and MySQL/MariaDB, all running on localhost and only exposed through Tor. Part 1 covered manual CLI setup (installing packages, editing configs, using mysql_secure_installation
, UFW, fail2ban, etc.), and Part 2 showed how to accomplish the same tasks through the Webmin web interface. Throughout, we emphasize security hardening: new sudo user, disable root SSH, enable UFW (only SSH port open by default), install fail2ban, and bind all services to 127.0.0.1
so they’re only reachable via Tor. Mapping Webmin to its own onion address lets you manage the server privately as well.
Your server is now set up for static and dynamic content (WordPress, PHP apps, etc.) on Tor. Always keep software updated and monitor logs. For more details on hidden services and best practices, see the Tor Project’s documentation.
Sources: Official Tor documentation for hidden services; DigitalOcean tutorials for LEMP and Webmin installation; community guides and gists on Tor and Nginx. These show the exact commands and configurations used above.
0 thoughts on “Installing Secure Tor Onion Hidden Services on Ubuntu (CLI and Webmin Guide)”