Simplified Service Deployments | Traefik labs

From buying a domain name to setting up the DNS and handing an exposed port on your ISP router, hosting a web application is often a hassle. Today, I want to show you how to deliver your application to your targeted audience in a matter of seconds using the Tor Network and Traefik Proxy.

But what's that Tor thing, you say?

The Tor Network was originally a project created by an MIT graduate to be used as a one-of-a-kind decentralized network that does not rely on big centralized entities to operate. This software is free and open to maximize transparency and decentralization.

Those of you who are already familiar with Tor, naturally have some reservations about its legal status. Allow me to put your mind at ease. The Tor Network is perfectly legal. That said, it can and has been used for illicit activities, so caution is always advised. Think of Tor as a knife — it is a powerful tool that can cause harm if used improperly.

Disclaimer: Traefik Labs does not condone any illicit acts performed using the Tor Network.

In this article, I am using the Tor Network purely for the purpose of exploring different use cases of Traefik Proxy. There is no official integration between Traefik Proxy and Tor. I will show you a way to configure Tor to expose a web application to the Tor Network via Traefik Proxy, and without touching any network configuration.

The source code for this blog post can be found here.

Prerequisites for this tutorial

Tor can work in many ways with Traefik Proxy. For the sake of this article, I assume that you have already installed and configured:

The entire stack that I'm about to deploy will be architectured like in the following diagram:

Here, Tor will serve as a VPN that allows you to build a tunnel from the Tor Network to the application. Doing so removes the pain of setting up other key components in classic hosting, like setting up DNS, routers NAT, and so on.

Creating the Tor Docker image

To get things started, let's create a Docker container and have Tor run as a service. Create a Dockerfile containing this:

FROM alpine:3.15
RUN apk --no-cache add tor
COPY ./torrc /torrc
USER tor
CMD ["-f", "/torrc"]

This Docker image contains the Tor binary and a torrc configuration file.

Check out man tor for more information on possible configurations of this file and take a look at this article for hosting best practices. For more in-depth configuration guides, make sure to take a look at the official Tor documentation.

But to our example, this torrc file should contain the following:

HiddenServiceDir /var/lib/tor/traefik/
HiddenServicePort 80 traefik:80
HiddenServicePort 443 traefik:443
HiddenServiceSingleHopMode 1
HiddenServiceNonAnonymousMode 1
SocksPolicy reject *
SocksPort 0

The three first lines define how Tor will transfer incoming requests to Traefik Proxy. Thanks to Docker, the Traefik Proxy service will be named traefik, and so the tor service will be able to contact Traefik Proxy directly via this domain name.

Note: I only set the 80 and 443 ports to limit incoming requests to these two ports. Feel free to open/close more ports according to your needs.

Use HiddenServiceSingleHopMode to run the tor service as non-anonymous to allow direct connections to Tor. HiddenServiceNonAnonymousMode is set to 1 as a requirement to enableHiddenServiceSingleHopMode.

I set SocksPolicy to prohibit the usage of this service as an entry node to the Tor Network from this host. SocksPort is set to 0 to disable all client-side services on this host.

Note: You may want to store (using a volume) the folder /var/lib/tor/traefik/ because it contains all Tor's necessary keys. The folder contains:

  • The hostname as the <base32-encoded-fingerprint>.onion domain name for this hidden service.
  • The private key for the symmetric cryptography of the hidden service.
  • The client_keys that contains the authorization data for the service to whitelist users.

Finally, let's use docker compose to host the deployment of my Docker services. I need to create a docker-compose.yml file, and fill it with the following:

version: '3.9'
    build: .

Deploying Traefik Proxy

Now that I can run Tor, it is time to use Tor's power to route requests to my web application.

First, I need to create a Traefik Docker container that will be able to accept requests from Tor and forward them to my web application. For this, I have to create a new service inside the docker-compose.yml file:

    image: traefik:v2.7
    container_name: traefik
      - --providers.docker
      - /var/run/docker.sock:/var/run/docker.sock:ro

Note: This configuration is the bare minimum required to use Traefik Proxy, and it might not be suited for running a production-grade web application.

The Traefik service does not publish port because all Tor requests will go through to Traefik Proxy thanks to Docker. I also name the traefik service to fix the connection between Tor and Traefik Proxy.

There is no need to use TLS as Tor already uses a complete encrypted tunnel and Perfect Forward Secrecy (PFS), so there's no need to use TLS. If you still want to do so, take a look at this guide and keep in mind that only Digicert or HARICA will be able to provide certificates in this case.

Firing up Tor and Traefik Proxy

Now that I have both Tor and Traefik services all set up, I can start both by running:

$ docker compose up -d --build

It is now time to get the hostname of my Tor endpoint to be later used when deploying a simple web application. To do so, I need to do the following:

$ docker compose exec tor cat /var/lib/tor/traefik/hostname

I need to keep this for later, to tell Traefik Proxy to match this hostname.

Setting up a web application

Let's create a simple example service.

    image: tommoulard/cat
      traefik.http.routers.web-app.rule: Host(`obz7zd7XXXXX.onion`)

Note: I placed my Tor hostname on the rule definition. This way, Traefik Proxy will know to forward requests coming from the Tor service to this web app.

Let's run this new service:

$ docker compose up -d web-app

Ta-da! I just created a Docker container and exposed it on Tor.

Go to your favorite Tor web browser on your Tor host service, type obz7zd7XXXXX.onion, and you should be able to see a beautiful cat, as your request came through to the web app!

Note: Your browser might say that this page is not secure, since the application uses images hosted on the internet, without the Tor encryption

A small bonus

Now that I have my web application accessible through the Tor network, let's show a little banner on my Tor enabled browser to tell users that my application is accessible through Tor.

For this, I have to add a special header to my non-Tor router. I created a header middleware that adds a special header for each response to my new router:

    image: tommoulard/cat
      # my Tor router
      traefik.http.routers.web-app-tor.rule: Host(`obz7zd7XXXXX.onion`)
      # my header middleware
      traefik.http.middlewares.tor-headers.headers.customresponseheaders.Onion-Location: "http://obz7zd7XXXXX.onion"
      # my classic router with a middleware
      traefik.http.routers.web-app-classic.middlewares: tor-headers
      traefik.http.routers.web-app-classic.rule: Host(``)

Going further

Now that you know how to connect Traefik Proxy to Tor, it will be easy to do much more than just expose a few cats. Feel free to use this knowledge to expose more useful services.

Please make sure to use the Tor Network responsibly and ethically!

I also recommend you visit the Traefik documentation to harness more of Traefik Proxy's power and explore more possibilities to build robust environments.

Bonus point: You can protect your services using the Fail2ban plugin!

Don't hesitate to reach out to our Community Forum with questions.

Master Traefik Proxy with K3s in our Free CourseLearn the ins and outs of using Traefik Proxy in edge Kubernetes environments.Sign Up Today

This is a companion discussion topic for the original entry at

Been meaning to get this setup for a while... thanks!

Btw in your examples Dockerfile and torrc have the name of the file as the first line and this breaks the configs.

Thanks @jack09 for this ! It's a typo introduced by the blogging engine, and that will be fixed soon !
You can see the correct file here: