Using Onionspray¶
This guide goes beyond the quick start setup laid out in the tutorial and covers the next steps after installation.
Check the command line syntax page for the full usage.
Project structure¶
As explained in the building blocks section, Onionspray sites are configured through projects.
Each project is composed of the following items:
- Onion Service keys stored at the
secrets/folder:- Yes, Onion Service keys from all projects are stored in the same folder.
- A project folder stored at
projects/<project-name>with:- HTTPS certificates, stored at
projects/<project-name>/ssl. - Log files stored at
projects/<project-name>/log. - Configuration files and scripts.
- HTTPS certificates, stored at
- A configuration file, like
<project-name>.conf:- This is were Onionspray is tuned for the specific project.
- Every time a project is reconfigured, scripts and configuration files in the project folder are updated according to this main configuration.
- An optional template, like
<project-name>.tconf:- This file is only used when
<project-name>.confdoes not exist. - The only difference between the template and a regular configuration file is that templates supports placeholders such as those to generate Onion Service addresses on the fly when configuring a project.
- This file is only used when
Creating a project¶
A project can be created in three ways:
- Using the
./onionspray createcommand, as explained in the tutorial. - Using a template file ending in
.tconf, when the Onion Service keys still need to be generated. - Using a configuration file ending in
.conf, when the Onion Service keys already exists.
All three ways ends up generating a .conf file, which can later be edited
and used to reconfigure a project.
Which way to use depends on what are your needs. When unsure, start with the
create command detailed in the tutorial.
The rest of this section will focus on the template (.tconf) approach.
Regarding the Onion Service keys, there are two ways to proceed:
- With automatically-generated addresses.
- With manually-mined (or pre-existing) addresses.
With automatically-generated Onions Service addresses¶
Random addresses¶
Random addresses are created with the %NEW_V3_ONION% placeholder
in project templates.
Create a config file with a .tconf suffix -- we'll pretend it's foo.tconf
-- and use this kind of syntax:
set project myproject
hardmap %NEW_V3_ONION% foo.com
hardmap %NEW_V3_ONION% foo.co.uk
hardmap %NEW_V3_ONION% foo.de
... and then run
./onionspray config foo.tconf
... which will create the onions for you and will populate a foo.conf for
you, and it will then configure Onionspray from that. You should probably
delete foo.tconf afterwards, since forcibly reusing it would trash your
existing onions.
Vanity addresses¶
Vanity addresses are created with the %NEW_V3_VANITY_ONION_%
placeholder in project templates. The syntax is slightly different from the
%NEW_V3_ONION% because %NEW_V3_VANITY_ONION_% requires an argument
specifying the initial text prefix for the generated .onion address.
Create a config file with a .tconf suffix -- we'll pretend it's foo.tconf
-- and use this kind of syntax:
set project myproject
hardmap %NEW_V3_VANITY_ONION_foo% foo.com
hardmap %NEW_V3_VANITY_ONION_bar% foo.co.uk
hardmap %NEW_V3_VANITY_ONION_baz% foo.de
... and then run
./onionspray config foo.tconf
... which will create the onions for you and will populate a foo.conf for
you, and it will then configure Onionspray from that. You should probably
delete foo.tconf afterwards, since forcibly reusing it would trash your
existing onions.
Note that the generated .onion for foo.com will begin with foo, since the
%NEW_V3_VANITY_ONION_foo% placeholder was used; similarly, the .onion for
foo.co.uk and foo.de will begin, respectively, with bar and baz.
With manually-mined Onion Service addresses¶
Generating the key¶
Random addresses¶
A random .onion address can be manually generated with onionspray genkey:
- Run
onionspray genkey-- it will print the name of the onion it generates. - Run it as many times as you wish/need.
Vanity addresses¶
Vanity addresses can be manually generated in a number of ways:
-
With
onionspray genkey-vanity <filter>:- Run
onionspray genkey-vanity <filter>-- it will print the name of the onion it generates. - Run it as many times as you wish/need.
- Example
onionspray genkey-vanity test-- it will generate an address beginning withtest. - This uses Onionmine under the hood.
- Run
-
Using and alternative tool or mkp224o directly:
- Make sure to store your mined keys in
secrets.d, naming the keys likesomeverylongonionaddressinvolvingalotofbase23characterss.v3pub.keyandsomeverylongonionaddressinvolvingalotofbase32characterss.v3sec.key
- Make sure to store your mined keys in
Check also the vanity guide for further details.
Creating the config file¶
-
Create a config file with a
.confsuffix - we'll pretend it'sfoo.conf- and use this kind of syntax, substitutinga2s3c4d5e6f7g8h9for the onion address that you generated.set project myproject hardmap z2walkyky3zzv52g3gedrhxa665qdrslyvrljg5wppibx34olslnxgqd foo.com
... and then (IMPORTANT) run:
./onionspray config foo.conf
... which will configure Onionspray.
Starting a project¶
Like this:
./onionspray start myproject
Starting all projects at once is also possible:
./onionspray start -a
Restarting a project¶
Restarting a project can be done with:
./onionspray restart myproject
To restart all projects, use
./onionspray restart -a
Reconfiguring a project¶
A project can be reconfigured by editing <project-name>.conf and
then running
./onionspray configure <project-name>.conf
Configuration options¶
Project settings are documented in the example configuration.
Subdomains management¶
Overview¶
Subdomains are supported like this, for dev as an example:
set project myproject
hardmap z2walkyky3zzv52g3gedrhxa665qdrslyvrljg5wppibx34olslnxgqd foo.com dev
... and if you have multiple subdomains:
hardmap z2walkyky3zzv52g3gedrhxa665qdrslyvrljg5wppibx34olslnxgqd foo.com dev blogs dev.blogs [...]
How it works¶
When you are setting up the mappings in a config file, you may have to accommodate "subdomains"; the general form of a internet hostname is like this:
hostname.domain.tld# like: www.facebook.com or www.gov.uk- or:
hostname.domain.sld.tld# like: www.amazon.co.uk hostname.subdom.domain.tld# like: www.prod.facebook.comhostname.subsubdom.subdom.domain.tld# cdn.lhr.eu.foo.nethostname.subsubsubdom.subsubdom.subdom.domain.tld# ...
... and so on, where:
- tld = top level domain
- sld = second level domain
- domain = generally the name of the organisation you are interested in
- subdomain = some kind of internal structure
- hostname = actual computer, or equivalent
When you are setting up mappings, generally the rules are:
- you will map one domain per onion
- you will ignore all hostnames
- you will append all possible subdomain stems
So if your browser tells you that you are fetching content from
cdn7.dublin.ireland.europe.foo.co.jp, you should add a line like:
hardmap %NEW_V3_ONION% foo.co.jp europe ireland.europe dublin.ireland.europe
... and Onionspray should do the rest. All this is necessary purely for correctness of the self-signed SSL-Certificates - which are going to be weird, anyway - and the rest of the HTML-rewriting code in Onionspray will be blind to subdomains.
Managing lots of sites and subdomains¶
Example:
www.foo.com.auwww.syd.foo.com.auwww.per.foo.com.au,www.cdn.foo.netwww.foo.aws.amazon.com- ...
Put them all in the same project as separate mappings, remembering to avoid the actual "hostnames" as described above:
set project fooproj
hardmap %NEW_V3_ONION% foo.com.au syd per
hardmap %NEW_V3_ONION% foo.net cdn
hardmap %NEW_V3_ONION% foo.aws.amazon.com
Onion mapping/translations will be applied for all sites in the same project.
System startup and housekeeping procedures¶
Once you have installed Onionspray and configured and tested it for your project, it's time to make sure the needed services will starting during system initialization.
There are three approaches to do this.
Init-like startup scripts¶
Onionspray generates init-like startup scripts through this command:
./onionspray make-scripts
This will create two files:
./onionspray-init.sh: for installing on your system as a startup script./onionspray-housekeeping.sh: for cronjob log rotation and other cleanup work
Please read the individual files for installation instructions; local setup is intended to be pretty simple.
Setting up a systemd service file¶
For Systemd, you'll have to create a file under
/etc/systemd/system/onionspray.service with proper ownership/permissions
(usually root:root and 0644), and activate it.
Example based on the Onionspray Ansible Role systemd configuration (adapt to where Onionspray is installed):
#
# Onionspray Systemd service file
#
# Copyright (C) 2025 The Tor Project, Inc.
# SPDX-License-Identifier: AGPL-3.0-or-later
#
[Unit]
Description=Onionspray
[Service]
Type=oneshot
ExecStart=/home/onionspray/onionspray/onionspray-init.sh start
ExecStop=/home/onionspray/onionspray/onionspray-init.sh stop
RemainAfterExit=true
[Install]
WantedBy=multi-user.target
Once this file has been set, proceed by enabling it:
sudo systemctl daemon-reload
sudo systemctl start onionspray
sudo systemctl enable onionspray
Using cron to manage Onionspray¶
Another approach for system initialization is using cron.
The following example is based on Onionspray Ansible Role cron configuration.
Create a /etc/cron.d/onionspray-service with proper ownership/permissions
(usually root:root and 0644) and the following contents (adapt to where
Onionspray is installed and to the corresponding user):
#
# Crontab acting as Onionspray's service initialization
#
# Copyright (C) 2025 The Tor Project, Inc.
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Use bash to run commands
SHELL=/bin/bash
# Start Onionspray on system reboot
@reboot onionspray (cd /home/onionspray ; ./onionspray clean -a ; ./onionspray bounce -a) &>/dev/null
Whenever the machine reboots and cron starts running, it will initialize
Onionspray.
Log rotation¶
Log rotation can be configured in Onionspray using two approaches.
Built-in log rotation¶
All existing logs in Onionspray can be rotated with:
./onionspray logrotate
This command does not rotates logs periodically, so it needs to be included in a regular procedure. For the init-like startup procedure, it is already invoke by the "house keeping" script.
Using logrotate directly¶
Another approach for log rotation is to use a specialized tool such as logrotate, which is available as a package for a number of systems.
The following example is based on the Onionspray Ansible Role logrotate configuration.
First, create a /etc/logrotate.d/onionspray with proper ownership/permissions
(usually root:root and 0644) and the following contents (adapt to your
needs, like where Onionspray is installed, the user running Onionspray,
rotation frequencies and number of times):
#
# Onionspray log rotation config
#
# Copyright (C) 2025 The Tor Project, Inc.
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Main logs
/home/onionspray/log/*.log {
su onionspray onionspray
create 644 onionspray onionspray
weekly
dateext
rotate 52
missingok
ifempty
compress
}
# Onionbalance logs
/home/onionspray/onionbalance/*.log {
su onionspray onionspray
create 644 onionspray onionspray
weekly
dateext
rotate 52
missingok
ifempty
compress
lastaction
/home/onionspray/onionspray ob-reload -a
endscript
}
# Project logs
/home/onionspray/projects/*/log/*.log {
su onionspray onionspray
create 644 onionspray onionspray
daily
dateext
rotate 365
missingok
ifempty
compress
lastaction
# Reload Onionspray
/home/onionspray/onionspray --local nxreload -a
/home/onionspray/onionspray --local torreload -a
endscript
}
This file should be enough if rotation intervals are equal or greater than a day, which is the usual frequency logrotate runs.
If you need smaller intervals, such as hourly rotations, make sure to also
create the /etc/cron.d/onionspray-logs file with proper ownership/permissions
(usually root:root and 0644) and the following contents:
#
# Crontab for Onionspray's log rotation
#
# Copyright (C) 2025 The Tor Project, Inc.
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Use bash to run commands
SHELL=/bin/bash
# Trigger logrotate for each Onionspray project every hour
0 * * * * root /usr/sbin/logrotate /etc/logrotate.d/onionspray &>/dev/null