The Tor service gives every machine a .onion address: a stable, cryptographically unique identifier that works anywhere Tor is running. Machines are reachable through the Tor network without public IP addresses, open firewall ports, or any DNS configuration.
Tor has the lowest priority (10) of all Clan networking services. Clan only uses it after every other configured networking service has failed. It is designed as a last-resort fallback: always available in theory, but the slowest option by a wide margin.
Use Tor when:
This service is experimental and will change in the future.
When you assign a machine the server role, Clan generates a unique Ed25519 key pair for it and configures a Tor version 3 onion service. The .onion hostname is derived from the key pair and managed as a var. Clan distributes the hostname to any machine that needs to connect and uses it automatically when falling back to Tor.
By default, the hostname is stored as a secret var and does not appear in plaintext in your configuration. This is recommended: anyone who discovers the .onion address could attempt brute-force attacks against the SSH service.
Tor provides two roles, client and server. You can read more about Tor's roles here.
# clan.nix
inventory.instances = {
tor = {
roles.server.tags = [ "all" ];
};
}; This is all you need. After running clan vars generate and deploying, every machine has a Tor onion address and Clan can use it as a fallback automatically.
This setup uses the internet service for primary connectivity and Tor as a fallback for all machines. The admin laptop gets the client role because it frequently connects to other machines and benefits from a persistent Tor proxy.
# clan.nix
inventory.machines = {
web-server = { tags = [ "server" ]; };
db-server = { tags = [ "server" ]; };
backup-server = { tags = [ "server" ]; };
admin-laptop = { tags = [ "laptop" ]; };
};
inventory.instances = {
# Primary: direct SSH for servers with public addresses
internet = {
roles.default.machines."web-server".settings.host = "web.example.com";
roles.default.machines."db-server".settings.host = "db.example.com";
roles.default.machines."backup-server".settings.host = "backup.example.com";
};
# Fallback: Tor for all machines
tor = {
roles.server.tags = [ "all" ];
# Keep a persistent Tor proxy on the admin laptop
roles.client.machines."admin-laptop" = {};
};
}; In this case, we have two services, Internet and Tor. By default, Internet has a higher priority, meaning when connecting, Clan will first try direct SSH through the Internet service. If a server is unreachable at its public address, Clan falls back to Tor.
To try out the Tor feature, create two cloud servers using the initial part of the Getting Started guide for a cloud (such as AWS or or Heztner), including adding your id_ed25519 key pair.
Create the clan, calling it CLAN-TOR:
nix run https://clan.lol/install/unstable --refresh -- init
cd CLAN-TOR
direnv allow and then create the two machines:
clan machines create web-server
clan machines create db-server Update clan.nix to look like the following:
{
# Ensure this is unique among all clans you want to use.
meta.name = "CLAN-TOR";
meta.domain = "clantor.lol";
inventory.machines = {
web-server = {
tags = [ "test" ];
};
db-server = {
tags = [ "test" ];
};
};
inventory.instances = {
internet = {
roles.default.machines."web-server" = {
settings.host = "44.243.115.151"; # REPLACE WITH YOUR MACHINE'S IP ADDRESS
settings.user = "root";
};
roles.default.machines."db-server" = {
settings.host = "35.95.10.21"; # REPLACE WITH YOUR MACHINE'S IP ADDRESS
settings.user = "root";
};
};
tor = {
roles.server.tags = [ "all" ];
# Keep a persistent Tor proxy on the admin laptop
roles.client.machines."web-server" = { };
};
# Docs: https://clan.lol/docs/services/official/sshd
# SSH service for secure remote access to machines.
# Generates persistent host keys and configures authorized keys.
sshd = {
roles.server.tags.all = { };
roles.server.settings.authorizedKeys = {
# Insert the public key that you want to use for SSH access.
# All keys will have ssh access to all machines ("tags.all" means 'all machines').
# Alternatively set 'users.users.root.openssh.authorizedKeys.keys' in each machine
"admin-machine-1" =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAZGMNlooljzJfmzQKaVcmj4tRYW+gqBIfdWbG0NU3XL freckleface@freckleface--Laptop";
};
};
# Docs: https://clan.lol/docs/unstable/services/official/users
# Root password management for all machines.
user-root = {
module = {
name = "users";
};
roles.default.tags.all = { };
roles.default.settings = {
user = "root";
prompt = true;
};
};
};
# Additional NixOS configuration can be added here.
# machines/server/configuration.nix will be automatically imported.
# See: https://clan.lol/docs/unstable/guides/inventory/autoincludes
machines = {
# test-machine = { config, ... }: {
# environment.systemPackages = [ pkgs.asciinema ];
# };
};
} Gather the hardware configuration:
clan machines init-hardware-config web-server
clan machines init-hardware-config db-server (For AWS, you'll add on --target-host ubuntu@<IP-ADDRESS> to each line.)
Add a disk configuration using the usual approach of re-running each command with the generated output added inside double-quotes:
clan templates apply disk ext4-single-disk web-server --set mainDisk ""
clan templates apply disk ext4-single-disk db-server --set mainDisk "" And finally, install:
clan machines install web-server
clan machines install db-server Or, if you already installed, update:
clan machines update If you get an error about sandboxing:
clan vars generate web-server --no-sandbox
clan vars generate db-server --no-sandbox Now you can obtain the .onion addresses for the two servers:
clan vars get web-server tor_tor/hostname
clan vars get db-server tor_tor/hostname You will see two ".onion" lines similar to the following:
kfobkkl3qexgyvzkwvj47f2gwo4vzgnmgk2xrkasmpgext2rblo6pgid.onion
tttwjmew6syhhqixakmv55jmbcsogeyar3anryfw4jaenmyzu7xemlqd.onion Next, to see if the onion service is running, ssh into one of the two servers and type:
systemctl status tor Your should see output that includes the first few lines looking similar to this, including the words "Active: active (running)".
tor.service - Tor Daemon
Loaded: loaded (/etc/systemd/system/tor.service; enabled; preset: ignored)
Active: active (running) since Thu 2026-07-02 18:43:39 UTC; 10min ago Exit back to your own computer. Now you can see if Tor was installed successfully by using torsocks to SSH into one of the two servers using its .onion address:
torsocks ssh root@kfobkkl3qexgyvzkwvj47f2gwo4vzgnmgk2xrkasmpgext2rblo6pgid.onion You will then see a normal SSH shell, from which you can exit:
[root@web-server:~]# exit Tip: If you need to install torsocks, you can do so with:
sudo apt update
sudo apt install torsocks tor -y By default, the onion service maps port 22 to port 22. If your SSH daemon listens on a different port, adjust the mapping:
# clan.nix
inventory.instances = {
tor = {
roles.server.machines."my-server" = {
settings.portMapping = [
{ port = 22; target.port = 2222; }
];
};
};
}; This exposes port 22 on the onion address and forwards it to port 2222 on the machine.
The secretHostname setting defaults to true, keeping the .onion address out of plaintext configuration. This is strongly recommended for any machine exposed to the internet.
In a controlled internal environment where you are certain exposure of the address isn't a concern, you can disable it:
# clan.nix
inventory.instances = {
tor = {
roles.server.machines."internal-machine" = {
settings.secretHostname = false;
};
};
}; Clan handles distributing onion addresses to machines that need them. You don't manage or share the hostname yourself.