SimpleX Without Tears: Wrapped in Onion, Served via Tor
After getting my own SimpleX SMP server running behind Traefik, thanks to my good friend and fellow blogger, I just had to think: "Okay, but why not make it accessible via the darknet?"
Well, now it is.
SimpleX already comes with strong privacy by design. But when you add Tor into the mix, you’re pulling your relay one step further out of the reach of traditional tracking. Hosting your SMP server as a Tor hidden service means: no DNS resolution, no traceable IP, and a juicy .onion
address to connect with your most paranoid contacts.
So here's how I set it up. No plugins or mystery scripts. It's just Docker, a custom Tor container, and a few tweaks to make the two talk nicely.
Why Even Bother with Tor?
- Anonymity: Your public IP is no longer exposed, not even to SimpleX SMP servers.
- Privacy: All relay connections go over the Tor network.
- Compatibility: SimpleX can use
.onion
relay addresses just fine, as long as you're connected to the Tor network.
Also… it’s just kinda cool.
Deployment
docker-compose.yml
Add the following snippet to your existing simplex smp server compose file. smp_tor:
build:
context: .
container_name: simplex_smp_tor
restart: unless-stopped
environment:
HIDDEN_PORT: 5223
SERVICE_PORT: 5223
volumes:
- $SMP_SERVER_TOR_ONION:/var/lib/tor
- $SMP_SERVER_TOR_SOCKS:/var/lib/socks
network_mode: service:smp_server
Dockerfile
FROM debian:bookworm
ENV DEBIAN_FRONTEND=noninteractive
RUN groupadd -g 101 debian-tor && \
useradd -u 101 -g debian-tor -d /var/lib/tor -s /usr/sbin/nologin -r debian-tor
RUN apt-get update && apt-get install -y sudo curl gnupg lsb-release ca-certificates gettext-base
RUN export CODENAME=$(lsb_release -c | awk '{print $2}') && \
echo "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org $CODENAME main" > /etc/apt/sources.list.d/tor.list && \
echo "deb-src [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org $CODENAME main" >> /etc/apt/sources.list.d/tor.list
RUN curl -fsSL https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc \
| gpg --dearmor -o /usr/share/keyrings/tor-archive-keyring.gpg
RUN apt-get update && apt-get install -y tor deb.torproject.org-keyring
RUN mkdir -p /var/lib/tor/onion /var/log/tor && \
chown -R debian-tor:debian-tor /var/lib/tor /var/log/tor && \
chmod 700 /var/lib/tor/onion
COPY torrc.template /etc/tor/torrc.template
RUN tor-instance-create socks
RUN mkdir -p /var/lib/socks && \
chown -R debian-tor:debian-tor /var/lib/socks
COPY torrc-socks /etc/tor/instances/socks/torrc
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
entrypoint.sh
#!/bin/bash
set -e
# Trap SIGTERM and SIGINT to kill child processes
trap 'kill $(jobs -p)' SIGTERM SIGINT
# Generate torrc with root permissions
envsubst </etc/tor/torrc.template >/etc/tor/torrc
# Change ownership in case it's needed
chown debian-tor:debian-tor /etc/tor/torrc
# Drop to debian-tor and run tor processes
sudo -u debian-tor tor -f /etc/tor/torrc &
sudo -u debian-tor tor -f /etc/tor/instances/socks/torrc &
# Wait for all background jobs
wait
torrc.template
Log notice file /var/log/tor/notices.log
SOCKSPort 0
HiddenServiceNonAnonymousMode 1
HiddenServiceSingleHopMode 1
HiddenServiceDir /var/lib/tor/onion/
HiddenServicePort ${HIDDEN_PORT} localhost:${SERVICE_PORT}
torrc-socks
# Log tor to stdout (since no syslog in container)
Log notice stdout
# Listen for SOCKS proxy
SocksPort 9050
# Use a separate DataDirectory
DataDirectory /var/lib/socks
How It Works
The Tor container runs as a sidecar to your smp_server
container by using network_mode: service:smp_server
. This lets Tor bind directly to the same localhost interface as the SMP server, so it can expose it as a hidden service. No reverse proxies or extra bridges are needed.
You get a .onion
address (saved in your onion
volume), and clients connected to the Tor network can use that as a relay address in SimpleX just like they would a normal domain.
It’s also worth noting:
- The SOCKS proxy is spun up alongside the hidden service so that your clients or bots (if needed) can also route outbound traffic via Tor from the same container.
- The setup supports volume binding for persistence — your
.onion
address won’t change unless you wipe the directory.
How to Use the .onion
Address
After the container runs once, your .onion
address will be available at:
docker exec simplex_smp_tor cat /var/lib/tor/onion/hostname
To use it:
- Give the
.onion
address to anyone you want to share your relay with. - In SimpleX, they can set it as a relay manually in the app if they have Tor enabled locally, or run their own socks proxy.
Settings -> Network & servers -> Your servers -> Add server -> Enter server manually
smp://<fingerprint>[:<password>]@<public_hostname>,<onion_hostname>:<port>
If you would like more information on hosting your own SimpleX SMP server, please visit the official documentation.