Skip to main content

Set up TOR with Docker

Set up TOR

You should now be the in the root directory of our LND-With-Docker folder. Run the pwd command to see the present working directory, and you should see something like this:

$ pwd

Take some time to look at the Tor Dockerfile

It's essential that you understand the basics of Docker. If you are totally confused right now and don't understand any of this Docker stuff, go back to Introducing Docker.

Here is the Dockerfile for tor-socks-proxy

FROM alpine:3.19

LABEL maintainer="Peter Dave Hello <>"
LABEL name="tor-socks-proxy"
LABEL version="latest"

RUN echo '@edge' >> /etc/apk/repositories && \
echo '@edge' >> /etc/apk/repositories && \
apk -U upgrade && \
apk -v add tor@edge obfs4proxy@edge curl && \
chmod 700 /var/lib/tor && \
rm -rf /var/cache/apk/* && \
tor --version
COPY --chown=tor:root torrc /etc/tor/

HEALTHCHECK --timeout=10s --start-period=60s \
CMD curl --fail --socks5-hostname localhost:9150 -I -L 'https://www.facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion/' || exit 1

USER tor
EXPOSE 8853/udp 9150/tcp

CMD ["/usr/bin/tor", "-f", "/etc/tor/torrc"]

You don't need to understand all of this, but you DO need to understand the basics: You're looking at a very clear, very minimal set of steps to create an entire little "computer". For example:

FROM alpine:3.19 ===> This establishes the base operating system

RUN echo '@edge https://dl-cdn.alpinelinux.... ===> This command downloads and installs software

CMD ["/usr/bin/tor", "-f", "/etc/tor/torrc"] ===> This specifies which commands will be run on "boot" of the container

Why we are using Docker Compose

There (at least) two different ways you can build and run a Dockerfile.

As you were learning Docker, you undoubtedly saw long, complicated Docker commands. For example:

docker build -f Dockerfile -t tor-socks-image . && docker run --name tor-socks-dockerfile --network host -v "$(pwd)/tor-data:/data" -v "$(pwd)/torrc:/etc/tor/torrc" -v "$(pwd)/../tmp:/tmp" -e HOME="/tmp" tor-socks-image

These commands do work, but I would encourage you to never run a Docker container with a long command like this. Reasons:

  • Long commands like this are extremely difficult to read and understand.
  • You would have to remember to write down this command somewhere, and then again in the future, when you want to run it again, you'd need remember where you wrote it down.

Instead of commands like the above, in this project we are going to always run Docker with Docker Compose. This will allow us to create verbose, highly-readable "Compose" files, which are also self-documenting.

Review the Docker Compose file for TOR

Open tor-socks-proxy/dtor.yml with your editor, or review the same file on GitHub:

The first thing to note: this code is specifying the same parameters as the long, complicated Docker command above. But it's much more readable, right?

Let's go through the important lines.

You see that under services:, only one service is defined: tor.

In build:, we see two keys:

dockerfile: Dockerfile
context: .

The dockerfile: directive points to our local Dockerfile.

The context: tells the docker engine that, when it runs commands in the Dockerfile, that you will run them from the directory ".", that is, the current directory.

The trickiest -- but also the most important -- part of dtor.yml are the volumes:.

Let's just look at one of the volumes:

- type: bind
source: ./tor-data
target: /data

The type is bind, meaning, we have mapped the local folder ./tor-data into the folder in the container found at /data.

Put another way, in the context of the overall project, the LND-With-Docker/tor-socks-proxy/tor-data folder will appear in the container's filesystem at this, more simple path: /data

Finally, let's look at these:

HOME: "/tmp"
network_mode: host

Under the environment key, we've specified one environment variable which will be active inside the container. This one variable is HOME, and its value is /tmp.

The network_mode set to host allows the container to directly use this computer's networking stack, as if it were running software directly on the computer.


Just one more file to review...

Review the torrc configuration file

Review the tor-socks-proxy/torrc file, or see it on GitHub, here:

This file specifies the ports that Bitcoin Core and LND will use to talk to LND and Bitcoin Core. It also, with the directive CookieAuthentication 1, tells TOR to use a cookie file for authentication.

Let's take a moment to understand how LND is going to authenticate with TOR.

Understand a bound volume

If you look back at the Docker Compose file (dtor.yml), you'll see this volume:

- type: bind
source: ../tmp
target: /tmp

and this environment variable

HOME: "/tmp"

You can see that it is mounting ../tmp, which is the tmp folder in the parent LND-With-Docker directory, into /tmp in the container.

And with the HOME environment variable, TOR is instructed to keep its cookie file in this /tmp folder.

It's through this clever strategy that TOR is going to be able to share the tmp folder -- and in the folder, a cookie -- with LND!

Start the TOR container.

Now is the moment of truth!

From within the LND-With-Docker/tor-socks-proxy folder, run this command to start the TOR container:

docker compose -f dtor.yml up

You should see that the container builds, and then you should see log lines like

tor-socks-proxy-tor-1  | Mar 27 13:38:56.000 [notice] Bootstrapped 56% (loading_descriptors): Loading relay descriptors
tor-socks-proxy-tor-1 | Mar 27 13:39:15.000 [notice] Bootstrapped 61% (loading_descriptors): Loading relay descriptors
tor-socks-proxy-tor-1 | Mar 27 13:39:15.000 [notice] Bootstrapped 70% (loading_descriptors): Loading relay descriptors
tor-socks-proxy-tor-1 | Mar 27 13:39:17.000 [notice] Bootstrapped 75% (enough_dirinfo): Loaded enough directory info to build circuits

Great! Now, for just a little housekeeping.

Learn how to see running containers

It's important that you can at any time see a list of containers running on your machine.

Keep the TOR container running, and open a new terminal window with CNTRL-ALT-T

In this new window, use this command:

$ docker ps

This should make an output like this:

CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS                             PORTS     NAMES
4d81117327a3 tor-socks-proxy-tor "/usr/bin/tor -f /et…" 29 seconds ago Up 28 seconds (health: starting) tor-socks-proxy

So here we can see the container is running.

Learn how to stop a running container

Now let's try stopping this running container.

Use this command

$ docker stop tor-socks-proxy

When you run this, you should see that the running TOR container, in your first terminal window, is stopped. (You can also stop a running container by hitting CNTRL-C in the same terminal window where the container is running.)

Next, close this terminal window #2, so you should now again have only one terminal window open on your machine.

Run TOR with the start script

To keep things tidy, for every container that I run, I like to setup an associated start script that I can run from the root of my project. This saves you the necessity of using cd to navigate to each subfolder individually.

Go back to the LND-With-Docker folder with the command cd ..

Now let's look at the script:

docker compose -f ./tor-socks-proxy/dtor.yml down
docker compose -f ./tor-socks-proxy/dtor.yml up

Just so you understand, you can see that we are doing the down command before the up command.

I always do down before an up in my start scripts, so that if the container is already running, it always stopped before we try to run it again.

Next, try running this script, with the command

$ ./

Great! Now, your Ubuntu desktop should have one (1) terminal window open, with TOR running.

Please leave this container running. Next, we will be starting up Bitcoin Core.