# Technical Documentation: haugene/docker-transmission-openvpn

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/haugene/docker-transmission-openvpn)

### Overview (`README.md`)

# OpenVPN and Transmission with WebUI

[![Docker Build](https://img.shields.io/github/actions/workflow/status/haugene/docker-transmission-openvpn/docker-image-builds.yml
)](https://hub.docker.com/r/haugene/transmission-openvpn/)
[](https://hub.docker.com/r/haugene/transmission-openvpn/)

This container contains OpenVPN and Transmission with a configuration
where Transmission is running only when OpenVPN has an active tunnel.
It has built-in support for many popular VPN providers to make the setup easier.

## Read this first

The documentation for this image is hosted on GitHub pages:

https://haugene.github.io/docker-transmission-openvpn/

If you can't find what you're looking for there, please have a look
in the [discussions](https://github.com/haugene/docker-transmission-openvpn/discussions)
as we're trying to use that for general questions.

If you have found what you believe to be an issue or bug, create an issue and provide
enough details for us to have a chance to reproduce it or understand what's going on.
**NB:** Be sure to search for similar issues (open and closed) before opening a new one.

## Quick Start

These examples show valid setups using PIA as the provider for both
docker run and docker-compose. Note that you should read some documentation
at some point, but this is a good place to start.

### Docker run

```
$ docker run --cap-add=NET_ADMIN -d \
              -v /your/storage/path/:/data \
              -v /your/config/path/:/config \
              -e OPENVPN_PROVIDER=PIA \
              -e OPENVPN_CONFIG=france \
              -e OPENVPN_USERNAME=user \
              -e OPENVPN_PASSWORD=pass \
              -e LOCAL_NETWORK=192.168.0.0/16 \
              --log-driver json-file \
              --log-opt max-size=10m \
              -p 9091:9091 \
              haugene/transmission-openvpn
```

### Podman run

Beware: container is run as privileged, meaning it has full access to host OS.

```
$ podman run --privileged -d \
              -v /your/storage/path/:/data \
              -v /your/config/path/:/config \
              -e OPENVPN_PROVIDER=PIA \
              -e OPENVPN_CONFIG=france \
              -e OPENVPN_USERNAME=user \
              -e OPENVPN_PASSWORD=pass \
              -e LOCAL_NETWORK=192.168.0.0/16 \
              --log-driver k8s-file \
              --log-opt max-size=10m \
              -p 9091:9091 \
              haugene/transmission-openvpn
```

### Docker version 3.x Compose
```
version: '3.3'
services:
    transmission-openvpn:
        cap_add:
            - NET_ADMIN
        volumes:
            - '/your/storage/path/:/data'
            - '/your/config/path/:/config'
        environment:
            - OPENVPN_PROVIDER=PIA
            - OPENVPN_CONFIG=france
            - OPENVPN_USERNAME=user
            - OPENVPN_PASSWORD=pass
            - LOCAL_NETWORK=192.168.0.0/16
        logging:
            driver: json-file
            options:
                max-size: 10m
        ports:
            - '9091:9091'
        image: haugene/transmission-openvpn
```

### Docker version 2.x Compose
```
version: "2.0"
services:
    transmission-openvpn:
        container_name: transmission
        cap_add:
            - NET_ADMIN
        volumes:
            - '/your/storage/path/:/data'
            - '/your/config/path/:/config'
        environment:
            - OPENVPN_PROVIDER=PIA
            - OPENVPN_CONFIG=france
            - OPENVPN_USERNAME=user
            - OPENVPN_PASSWORD=pass
            - LOCAL_NETWORK=192.168.0.0/16
        logging:
            driver: "json-file"
            options:
                max-size: 10m
        ports:
            - 9091:9091
        image: haugene/transmission-openvpn
```

## Known issues

If you've been running a stable setup that has recently started to fail, please check your logs.
Are you seeing `curl: (6) getaddrinfo() thread failed to start` or `WARNING: initial DNS resolution test failed`?
Then have a look at [#2410](https://github.com/haugene/docker-transmission-openvpn/issues/2410)
and [this comment](https://github.com/haugene/docker-transmission-openvpn/issues/2410#issuecomment-1319299598)
in particular. There is a fix and a workaround available.

## Image versioning

We aim to create periodic fixed releases with a [semver](https://semver.org/) versioning scheme.
The latest of the tagged fixed releases will also have the `latest` tag.

A semver release will be tagged with `major`, `major.minor` and `major.minor.patch` versions so that you can lock
the version at either level.

We also have a tag called `edge` which will always be the latest commit on `master`, and `dev` which is the last commit on the `dev` branch.
From time to time we can also have various `beta` branches and tags, but using either dev or beta tags is probably not for the average user
and you should expect there to be occasional breakage or even the deletion of the tags upstream.

## Please help out (about:maintenance)
This image was created for my own use, but sharing is caring, so it had to be open source.
It has now gotten quite popular, and that's great! But keeping it up to date, providing support, fixes
and new features take time. If you feel that you're getting a good tool and want to support it, there are a couple of options:

A small monthly amount through [](https://www.patreon.com/haugene) or
a one time donation with [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=73XHRSK65KQYC)

All donations are greatly appreciated! Another great way to contribute is of course through code.
A big thanks to everyone who has contributed so far!

---

### Index (`docs/index.md`)

<h1 align="center">
  OpenVPN and Transmission with WebUI
</h1>

<p align="center">
  Docker container running Transmission torrent client with WebUI over an OpenVPN tunnel
  <br/><br/>
  <a href="https://hub.docker.com/r/haugene/transmission-openvpn/">
    <img alt="pulls" src="https://img.shields.io/docker/pulls/haugene/transmission-openvpn.svg" />
  </a>
  <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=73XHRSK65KQYC">
    <img alt="Donate with PayPal" src="https://img.shields.io/badge/Donate-PayPal-green.svg">
  </a>
  <a href="https://www.patreon.com/haugene">
    <img alt="Donate with Patreon" src="https://github.com/haugene/docker-transmission-openvpn/raw/master/images/patreon.png">
  </a>
</p>

## Welcome to the documentation!

Writing good documentation is hard but we think that we're getting closer to a good place to search for answers.
These pages are not done yet and you will meet some "under construction" notices. Please bear with us and if you spot
mistakes or have suggestions - submit a PR :)

## General Information

* [The basic building blocks](building-blocks.md)
* [Running the container](run-container.md)
* [VPN and networking in containers](vpn-networking.md)
* [Supported providers and server locations](supported-providers.md)
* [Provider specific features/instructions](provider-specific.md)
* [Configuration options list](config-options.md)

## Troubleshooting

* [Frequently asked questions](faq.md)
* [Debugging your setup](debug.md)
* [Tips & Tricks](tips-tricks.md)

## Additional features

* [Web proxy: Privoxy](web-proxy.md)
* [RSS Plugin support](rss-plugin.md)

---

### Building Blocks (`docs/building-blocks.md`)

# The basic building blocks

## The goal

The core functionality of this image is to let the user run a
VPN tunnel and Transmission as easy as possible. Transmission
should only run while the VPN is active, and any disconnect
from VPN should cause Transmission to stop.

The container should provide community best practices on how to configure the kill switch, firewall and tweaks on the
OpenVPN configs to make it run as fast and secure as possible.

## It goes like this

To understand how it works, these are the most important events
and who/what starts them.

1. You start the container
2. The container starts OpenVPN
3. OpenVPN starts/stops Transmission

When you start the container it is instructed to run a script
to start OpenVPN. This is defined in [the Dockerfile](https://github.com/haugene/docker-transmission-openvpn/blob/master/Dockerfile).
This script is responsible for doing initial setup and preparing what is needed for OpenVPN to run successfully.

## Starting OpenVPN

The main purpose of the start-up script is to figure out which OpenVPN config to use.
OpenVPN itself can be started with a single argument, and that is the config file.
We also add a few more to tell it to start Transmission when the VPN tunnel is
started and to stop Transmission when OpenVPN is stopped. That's it.

Apart from that, the script does some firewall config, VPN interface setup and possibly other
things based on your settings. There are also some reserved script names that a user can mount/add to
the container to include their own scripts as a part of the setup or teardown of the container.

Anyways! You have probably seen the docker run and docker-compose configuration examples
and you've put two and two together: This is where environment variables come in.
Setting environment variables is a common way to pass configuration options to containers
and it is the way we have chosen to do it here.
So far, we've explained the need for `OPENVPN_PROVIDER` and `OPENVPN_CONFIG`. We use the
combination of these two to find the right config. `OPENVPN_CONFIG` is not set as a mandatory
option as each provider should have a default config that will be used if none is set.

With the config file identified we're ready to start OpenVPN, the only thing missing are probably
a username and password. There are some free providers out there, but they are the exceptions to the rule.
We must inject the username/password into the config somehow. Again there are exceptions but the majority
of configs from regular providers contain a line with `auth-user-pass` which will make OpenVPN prompt for username
and password when you start a connection. That will obviously not work for us, so we need to modify that option.
If it's followed by a path to a file, it will read the first line of that file as username and the second line as password.

You provide your username and password as `OPENVPN_USERNAME` and `OPENVPN_PASSWORD`. These will be
written into two lines in a file called `/config/openvpn-credentials.txt` on start-up by the start script.
Having written your username/password to a file, we can successfully start OpenVPN.

## Starting Transmission

We're using the `up` option from OpenVPN to start Transmission.
> --up cmd<br>
> &nbsp;&nbsp;&nbsp;&nbsp;Run command cmd after successful TUN/TAP device open

This means that Transmission will be started when OpenVPN has connected successfully and opened the tunnel device.
We are having OpenVPN call the [tunnelUp.sh](https://github.com/haugene/docker-transmission-openvpn/blob/master/openvpn/tunnelUp.sh)
script which in turn will call the start scripts for
[Transmission](https://github.com/haugene/docker-transmission-openvpn/blob/master/transmission/start.sh) and 
[Privoxy](https://github.com/haugene/docker-transmission-openvpn/blob/master/privoxy/scripts/start.sh).

The up script will be called with a number of parameters from OpenVPN, and among them is the IP of the tunnel interface.
This IP is the one we've been assigned by DHCP from the OpenVPN server we're connecting to.
We use this value to override Transmission’s bind address, so we'll only listen for traffic from peers on the VPN interface.

The start-up script checks to see if one of the [alternative web UIs](config-options.md#alternative_web_uis) should be used for Transmission.
It also sets up the user that Transmission should be run as, based on the PUID and PGID passed by the user
along with selecting preferred logging output and a few other tweaks.

Before starting Transmission we also need to see if any settings should be overridden.
One example of this is binding Transmission to the IP we've gotten from our VPN provider.
Here we check if we find any environment variables that match a setting that we also see in settings.json.
This is described in the [config section](config-options.md#transmission_configuration_options).
Setting a matching environment variable will then override the setting in Transmission.

OpenVPN does not pass the environment variables it was started with to Transmission.
To still be able to access them when starting Transmission, we're writing the ones we need to a file when starting OpenVPN.
That way we can read them back and use them here. With the environment variables in place
[this script](https://github.com/haugene/docker-transmission-openvpn/blob/master/transmission/updateSettings.py) then overwrites
the selected properties in settings.json and we're ready to start Transmission itself.

After starting Transmission there is an optional step that some providers have;
to get an open port and set it in Transmission. **Opening a port in your local router does not work**.
I made that bold because it's a recurring theme. It's not intuitive until it is, I guess.
Since all your traffic is going through the VPN, which is kind of the point, the port you have to open is not on your router.
Your router's external IP address is the destination of those packets. It is on your VPN provider’s end that it has to be opened.
Some providers support this, others don't. We try to write scripts for those that do, and that script will be executed
after starting Transmission if it exists for your provider.

At this point, Transmission is running, and everything is great!
But you might not be able to access it, and that's the topic of the [networking section](vpn-networking.md).

---

### Config Options (`docs/config-options.md`)

### Required environment options

| Variable           | Function                          | Example                                                                                                 |
| ------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `OPENVPN_PROVIDER` | Sets the OpenVPN provider to use. | `OPENVPN_PROVIDER=provider`. Supported providers and their config values are listed in the table above. |
| `OPENVPN_USERNAME` | Your OpenVPN username             | `OPENVPN_USERNAME=asdf`                                                                                 |
| `OPENVPN_PASSWORD` | Your OpenVPN password, beware of special characters. Docker run vs docker-compose (using YAML) interpret special characters differently, see  [Yaml special characters](https://support.asg.com/mob/mvw/10_0/mv_ag/using_quotes_with_yaml_special_characters.htm)             | `OPENVPN_PASSWORD=asdf`                                                                                 |

Docker secrets are available to define OPENVPN_USER and OPENVPN_PASSWORD.

* remove OPENVPN_USERNAME, OPENVPN_PASSWORD from environment.
* write your credentials in one file: openvpn_creds
* add to your compose YAML:

```yaml
version: '3.8'
services:
 transmission:
    ....
    secrets:
        - openvpn_creds

secrets:
    openvpn_creds:
        file: ./openvpn_creds
```

### Network configuration options

| Variable            | Function                                                                                            | Example                                                                                                        |
| ------------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `OPENVPN_CONFIG`    | Sets the OpenVPN endpoint to connect to. Accepts a comma-separated list of items (one item is selected at random.)  | `OPENVPN_CONFIG=UK Southampton,UK Liverpool`                                                                                |
| `OPENVPN_OPTS`      | Will be passed to OpenVPN on startup                                                                | See [OpenVPN doc](https://openvpn.net/index.php/open-source/documentation/manuals/65-openvpn-20x-manpage.html) |
| `LOCAL_NETWORK`     | Sets the local network that should have access. Accepts comma-separated list.                       | `LOCAL_NETWORK=192.168.0.0/24`                                                                                 |
| `CREATE_TUN_DEVICE` | Creates /dev/net/tun device inside the container, mitigates the need to mount the device from the host | `CREATE_TUN_DEVICE=true`                                                                                       |
| `OVERRIDE_DNS`      | Override the VPN-provided DNS servers. Use `OVERRIDE_DNS_1`, `OVERRIDE_DNS_2`, etc. for multiple servers. When set, VPN DNS will not be applied. | `OVERRIDE_DNS_1=8.8.8.8` `OVERRIDE_DNS_2=8.8.4.4`                                                             |

### Timezone option

Set a custom timezone in tz database format. Look [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for a list of valid timezones. Defaults to UTC.

| Variable | Function     | Example  |
| -------- | ------------ | -------- |
| `TZ`     | Set Timezone | `TZ=UTC` |

### Firewall configuration options

When enabled, the firewall blocks everything except traffic to the peer port and traffic to the rpc port from the LOCAL_NETWORK and the internal docker gateway.

If TRANSMISSION_PEER_PORT_RANDOM_ON_START is enabled then it allows traffic to the range of peer ports defined by TRANSMISSION_PEER_PORT_RANDOM_HIGH and TRANSMISSION_PEER_PORT_RANDOM_LOW.

| Variable                      | Function                                                                                                                    | Example                            |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| `ENABLE_UFW`                  | Enables the firewall                                                                                                        | `ENABLE_UFW=true`                  |
| `UFW_ALLOW_GW_NET`            | Allows the gateway network through the firewall. Off defaults to only allowing the gateway.                                 | `UFW_ALLOW_GW_NET=true`            |
| `UFW_EXTRA_PORTS`             | Allows the comma-separated list of ports through the firewall. Respects UFW_ALLOW_GW_NET.                                   | `UFW_EXTRA_PORTS=9910,23561,443`   |
| `UFW_DISABLE_IPTABLES_REJECT` | Prevents the use of `REJECT` in the `iptables` rules, for hosts without the `ipt_REJECT` module (such as the Synology NAS). | `UFW_DISABLE_IPTABLES_REJECT=true` |

### Health check option

Because your VPN connection can sometimes fail, Docker will run a health check on this container every 5 minutes to see if the container is still connected to the internet. By default, this check is done by pinging google.com once. You change the host that is pinged.

| Variable            | Function                                                           | Example      |
| ------------------- | ------------------------------------------------------------------ | ------------ |
| `HEALTH_CHECK_HOST` | this host is pinged to check if the network connection still works | `google.com` |

### Permission configuration options

By default, the startup script applies a default set of permissions and ownership on the transmission download, watch and incomplete directories. The GLOBAL_APPLY_PERMISSIONS directive can be used to disable this functionality.

| Variable                   | Function                               | Example                          |
| -------------------------- | -------------------------------------- | -------------------------------- |
| `GLOBAL_APPLY_PERMISSIONS` | Disable setting of default permissions | `GLOBAL_APPLY_PERMISSIONS=false` |

### Alternative Web UIs

This container comes bundled with some alternative Web UIs:

* [Combustion UI](https://github.com/Secretmapper/combustion)
* [Kettu](https://github.com/endor/kettu)
* [Transmission-Web-Control](https://github.com/ronggang/transmission-web-control/)
* [Flood for Transmission](https://github.com/johman10/flood-for-transmission)
* [Shift](https://github.com/killemov/Shift)
* [Transmissionic](https://github.com/6c65726f79/Transmissionic)

To use one of them instead of the default Transmission UI you can set `TRANSMISSION_WEB_UI`
to either `combustion`, `kettu`, `transmission-web-control`, `flood-for-transmission`, `shift` or `transmissionic` respectively.

| Variable                | Function                         | Example                                                                                                                                       |
| ----------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `TRANSMISSION_WEB_UI`   | Use the specified bundled web UI | `TRANSMISSION_WEB_UI=combustion` <br>`TRANSMISSION_WEB_UI=kettu` <br>`TRANSMISSION_WEB_UI=transmission-web-control` <br>`TRANSMISSION_WEB_UI=flood-for-transmission` <br>`TRANSMISSION_WEB_UI=shift` <br>`TRANSMISSION_WEB_UI=transmissionic` |

### User configuration options

By default, everything will run as the root user. However, it is possible to change who runs the transmission process.
You may set the following parameters to customize the user id that runs Transmission.

| Variable | Function                                    | Example     |
| -------- | ------------------------------------------- | ----------- |
| `PUID`   | Sets the user id who will run transmission  | `PUID=1003` |
| `PGID`   | Sets the group id for the transmission user | `PGID=1003` |

### Transmission configuration options

In previous versions of this container the settings were not persistent but were generated from environment variables on container startup.
This had the benefit of being very explicit and reproducible but you had to provide Transmission config as environment variables if you
wanted them to stay that way between container restarts. This felt cumbersome to many.

As of version 4.2, this is no longer true. Settings are now persisted in the `/config/transmission-home` folder in the container and as
long as you mount `/config` you should be able to configure Transmission using the UI as you normally would.
If you are using the container from earlier versions and have not changed the location of transmission-home to /config, you will see a warning message that the default has changed.
You can manually move the folder to your /config volume directory after stopping the container and adding the /config mount to your container setup (compose/run etc).

You may still override Transmission options by setting environment variables if that's your thing.
The variables are named after the transmission config they target but are prefixed with `TRANSMISSION_`, capitalized, and `-` is converted to `_`.

For example:

| Transmission variable name   | Environment variable name                 |
|------------------------------|-------------------------------------------|
| `speed-limit-up`             | `TRANSMISSION_SPEED_LIMIT_UP`             |
| `speed-limit-up-enabled`     | `TRANSMISSION_SPEED_LIMIT_UP_ENABLED`     |
| `ratio-limit`                | `TRANSMISSION_RATIO_LIMIT`                |
| `ratio-limit-enabled`        | `TRANSMISSION_RATIO_LIMIT_ENABLED`        |

A full list of variables can be found in the Transmission documentation [here](https://github.com/transmission/transmission/blob/main/docs/Editing-Configuration-Files.md#options).

All variables overridden by environment variables will be logged during startup.

PS: `TRANSMISSION_BIND_ADDRESS_IPV4` will automatically be overridden to the IP assigned to your OpenVPN tunnel interface.
This ensures that Transmission only listens for torrent traffic on the VPN interface and is part of the fail-safe mechanisms.

### Dropping default route from iptables (advanced)

Some VPNs do not override the default route, but rather set other routes with a lower metric.
This might lead to the default route (your untunneled connection) being used.

To drop the default route set the environment variable `DROP_DEFAULT_ROUTE` to `true`.

_Note_: This is not compatible with all VPNs. You can check your iptables routing with the `ip r` command in a running container.

### Changing logging locations

By default, Transmission will log to a file in `TRANSMISSION_HOME/transmission.log`.

To log to stdout instead set the environment variable `LOG_TO_STDOUT` to `true`.

_Note_: By default, stdout is what container engines read logs from. Set this to true to have Transmission logs in commands like `docker logs` and `kubectl logs`. OpenVPN currently only logs to stdout.

### Open files limit

Transmission needs one file descriptor per peer plus data, resume and log files. To avoid
`Too many open files` errors, the container raises the open-files (`nofile` / `RLIMIT_NOFILE`)
soft limit for the Transmission daemon before starting it.

By default it raises the soft limit to the container's hard limit. You can override the target
value with the `OPEN_FILES_LIMIT` environment variable, e.g. `OPEN_FILES_LIMIT=1048576`. Only the
soft limit is ever touched - the hard limit assigned by the runtime is left alone and acts as the
ceiling; see the [FAQ entry](faq.md#unable_to_save_resume_file_too_many_open_files) for how to
raise it.

### Custom scripts

If you ever need to run custom code before or after Transmission is executed or stopped, you can use the custom scripts feature.
Custom scripts are located in the `/scripts` directory which is empty by default.
To enable this feature, you'll need to mount the `/scripts` directory.

Once `/scripts` is mounted you'll need to write your custom code in the following bash shell scripts:

| Script                              | Function                                                     |
| ----------------------------------- | ------------------------------------------------------------ |
| /scripts/openvpn-pre-start.sh       | This shell script will be executed before OpenVPN starts      |
| /scripts/openvpn-post-config.sh     | This shell script will be executed after OpenVPN config      |
| /scripts/transmission-pre-start.sh  | This shell script will be executed before transmission starts |
| /scripts/transmission-post-start.sh | This shell script will be executed after transmission starts |
| /scripts/routes-post-start.sh       | This shell script will be executed after routes are added    |
| /scripts/transmission-pre-stop.sh   | This shell script will be executed before transmission stops  |
| /scripts/transmission-post-stop.sh  | This shell script will be executed after transmission stops   |

`transmission-pre-start.sh` is run from OpenVPN's `--up` handler (it calls `/etc/transmission/start.sh`). OpenVPN waits for that script to finish, so anything slow here can block VPN connection setup.

Don't forget to include the `#!/bin/bash` shebang and to make the scripts executable using `chmod a+x`

### Debugging

By default, commands are not echoed to stdout before being processed. Setting DEBUG to any value other than the string false will trigger command echo (set -x) for all bash scripts.

| Variable                              | Function |Example|
| ----------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|DEBUG|Echo all commands to stdout.|DEBUG=true|

---

### Debug (`docs/debug.md`)

# Debugging your setup

The goal of this page is to provide a common set of tests that can be run to try to narrow down
an issue with the container before you create a new issue for it. We see a lot of repeat
business in the issues section and spending time answering questions for individual setups takes
away from improving the container and making it more stable in the first place.

This guide should be improved over time but can hopefully help you point out the most common errors
and provide some pointers on how to proceed. A summary of what you've tried should be added to
the description if you can't figure out what's wrong with your setup and create an issue for it.

## Introduction and assumptions

I am going to assume that you have shell access to the host and that you can use `docker run`
commands to test it. If you're a docker-compose user then you can make a similar setup in docker-compose.
If you are using any of the NAS container orchestration UIs then you just have to mimic this behaviour
as best you can. Note that you can ssh into the NAS and run commands directly.

NOTE: The commands listed here use the --rm flag which will remove the container from the host when it
shuts down. And as we're not mounting any volumes here, your host system will not be altered from running
any of these commands. If any command breaks with this principle it will be noted.

## Checking that Docker works properly

For this container to work, you have to have a working Docker installation on your host.

We'll begin very simply with this command that will print a welcome message if Docker is properly installed.
```
docker run --rm hello-world
```

Then we can try to run an alpine image, install curl and run curl to get your public IP.
This verifies that Docker containers on your host have working internet access
and that they can look up hostnames with DNS.
```
docker run --rm -it alpine sh -c "apk add curl && curl ipecho.net/plain"
```

If you get an error with "Could not resolve host" then you have to look at the DNS options in
[the Docker run reference](https://docs.docker.com/engine/reference/run/#network-settings).

Finally, we will check that your Docker daemon runs with a bridge network as the default network driver.
The following command runs an alpine container and prints its iptable routes. It probably outputs two
lines and one of them starts with `172.x.0.0/16 dev eth0` and the other one also references `172.x.0.1`.
The 172 addresses are a sign that you're on a Docker bridge network. If your local IP like `192.168.x.y`
shows up your container is running with host networking and the VPN container would affect the entire host
instead of just affecting Transmission running within the container.
```
docker run --rm -it alpine ip r
```

If you have gotten any errors so far you have to refer to Docker documentation and other forums to get
help getting Docker to work.

## Try running the container with an invalid setup

We'll keep this brief because it's not the most useful step, but you can verify a bit anyways.

Run this command (even if PIA is not your provider) and do not insert your real username/password:
```
docker run --rm -it -e OPENVPN_PROVIDER=PIA -e OPENVPN_CONFIG=france -e OPENVPN_USERNAME=donald -e OPENVPN_PASSWORD=duck haugene/transmission-openvpn
```

At this point, the commands are getting longer. I'll start breaking them up into lines using \ to escape the line
breaks. For those that are new to shell commands; a \ at the end of the line will tell the shell to keep on
reading as if it was on the same line. You can copy-paste this somewhere and put everything on the same line
and remove the \ characters if you want to. The same command then becomes:
```
docker run --rm -it \
  -e OPENVPN_PROVIDER=PIA \
  -e OPENVPN_CONFIG=france \
  -e OPENVPN_USERNAME=donald \
  -e OPENVPN_PASSWORD=duck \
  haugene/transmission-openvpn
```

This command should fail and exit with three lines that look like this (I've trimmed it a bit):
```
Peer Connection Initiated with ...
AUTH: Received control message: AUTH_FAILED
SIGTERM[soft,auth-failure] received, process exiting
```

And this is not nothing. The container has made contact with the VPN server and they have agreed
that you have not provided correct authentication. So we're getting somewhere.

## Running with a valid configuration

Upgrading slightly from the last command we need to add our real provider, config and username/password.
This is what the container needs to be able to connect to VPN. The config is not a mandatory option and
all providers should have a default that is used if you don't set it. But I will set it here anyways as
I think it's good to know where and what you're connecting to.

The command is basically the same. I'm going to stick with PIA/France as I am a PIA user, but you should set
one of the [supported providers](supported-providers.md) or provide your own config using the 
[custom configuration option](supported-providers.md#using_a_custom_provider). Since I'm now expecting to connect
successfully to my VPN provider, I have to give the container elevated access to modify networking needed to
establish a VPN tunnel. I'll add the `--cap-add=NET_ADMIN` and you can read more about
that [here](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities).

Also because I'm using PIA and they support port forwarding which is automatically configured in this 
container, I will disable that script for now. It's unnecessary at this point and I don't want to introduce
more error sources than I have to.

```
docker run --rm -it --cap-add=NET_ADMIN \
  -e OPENVPN_PROVIDER=PIA \
  -e OPENVPN_CONFIG=france \
  -e OPENVPN_USERNAME=username \
  -e OPENVPN_PASSWORD=password \
  -e DISABLE_PORT_UPDATER=true \
  haugene/transmission-openvpn
```

The logs should be longer this time, and you should end up with `Initialization Sequence Completed` at the end.
If you don't see this message then look through your logs for errors and see if you can find your error in the
list in the [FAQ](faq.md). If the error is not easily understandable or listed in the FAQ, open a new issue on it.

## Checking if Transmission is running

We're continuing with the `docker run` command from the last example. Because we have not yet introduced the
LOCAL_NETWORK variable you cannot access Transmission that is running in the container. We have not exposed
any ports on your host either so Transmission is not reachable from outside of the container as of now.

You don't need to expose Transmission outside of the container to contact it though. You can get another shell
inside the same container that you are running and try to curl Transmission web UI from there.

If this gets too complicated then you can skip to the next point, but please try to come back here
if the next point fails. One thing is not being able to access Transmission which might be network related,
but if Transmission is not running it's something else entirely.

If you run `docker ps` you should get a list of all running docker containers. If the list is too long then
you can ask for only transmission-openvpn containers with `docker ps --filter ancestor=haugene/transmission-openvpn`.

Using the Container ID from that list you can run `docker exec -it <container-id> bash`.
Once you're in the container run `curl localhost:9091` and you should expect to get
`<h1>301: Moved Permanently</h1>` in return. This is because Transmission runs at /web/transmission and
tries to redirect you there. It doesn't matter because you now see that Transmission is running and
apparently doing well.

## Accessing Transmission Web UI

If you've come this far we hopefully will be able to connect to the Transmission Web UI from your browser.
To do this we have to know what LAN IP your system is on. The reason for this is a bit complex and
is described in the [VPN networking](vpn-networking.md) section. The short version is that OpenVPN needs to
be able to differentiate between what traffic to tunnel and what to let go. Since the VPN is running on
the Docker bridge network it is not able to detect computers on your LAN as actually being local devices.

We'll base ourselves on the command from the previous sections, but to access Transmission, we need to
expose the 9091 port to the host and tell the containers what IP ranges NOT to tunnel. Whatever you put
in LOCAL_NETWORK will be trusted as a local network and traffic to those IPs will not be tunnelled.
Here we will assume that you're on one of the common 192.168.x.y subnets.

The command then becomes:
```
docker run --rm -it --cap-add=NET_ADMIN \
  -p 9091:9091 \
  -e LOCAL_NETWORK=192.168.0.0/16 \
  -e OPENVPN_PROVIDER=PIA \
  -e OPENVPN_CONFIG=france \
  -e OPENVPN_USERNAME=username \
  -e OPENVPN_PASSWORD=password \
  -e DISABLE_PORT_UPDATER=true \
  haugene/transmission-openvpn
```

With any luck, you should now be able to access Transmission at [http://localhost:9091](http://localhost:9091)
or whatever server IP where you have started the container.

NOTE: If you're trying to run this alongside another container you can use `-p 9092:9091` to bind 9092
on the host instead of 9091 and avoid port conflict.

## Now what?

If this guide has failed at some point then you should create an issue for it. Please add the command
that you ran and the logs that were produced.

If you're now able to access Transmission and it seems to work correctly then you should add a volume mount
to the `/data` folder in the container. You'll then have a setup like what's shown on the
[main GitHub page](https://github.com/haugene/docker-transmission-openvpn/) of this project.

If you have another setup that does not work then you now have two versions to compare and maybe
that will lead you to find the error in your old setup. If the setup is the same but this version works then
the error is in your state. Transmission stores its state in /data/transmission-home by default and
it might have gotten corrupt. One simple thing to try is to delete the settings.json file that is found here.
We do mess with that file and we might have corrupted it. Apart from that, we do not change anything within
the Transmission folder and any issues should be asked in Transmission forums.

## Conclusion

I hope this has helped you to solve your problem or at least narrow down where it's coming from.
If you have suggestions for improvements do not hesitate to create an issue or even better open
a PR with your proposed changes.

---

### Faq (`docs/faq.md`)

* [The container runs, but I can't access the web ui](#the_container_runs_but_i_cant_access_the_web_UI)
* [How do I enable authentication in the web ui](#how_do_i_enable_authentication_in_the_web_UI)
* [How do I verify that my traffic is using VPN](#how_do_i_verify_that_my_traffic_is_using_vpn)
* [RTNETLINK answers: File exists](#rtnetlink_answers_file_exists)
* [RTNETLINK answers: Invalid argument](#rtnetlink_answers_invalid_argument)
* [TUNSETIFF tun: Operation not permitted](#tunsetiff_tun_operation_not_permitted)
* [Error resolving host address](#error_resolving_host_address)
* [Container loses connection after some time](#container_loses_connection_after_some_time)
  * [Set the ping-exit option for OpenVPN and restart-flag in Docker](#set_the_ping-exit_option_for_openvpn_and_restart-flag_in_docker)
  * [Use a third-party tool to monitor and restart the container](#use_a_third-party_tool_to_monitor_and_restart_the_container)
* [Send Username Password via file](#send_username_and_password_via_a_file)
* [AUTH: Received control message: AUTH_FAILED](#auth_received_control_message_auth_failed)
* [Unable to save resume file: Too many open files](#unable_to_save_resume_file_too_many_open_files)

## The container runs, but I can't access the web UI

[TODO](https://github.com/haugene/docker-transmission-openvpn/issues/1558): Short explanation and link to [networking](vpn-networking.md)

## How do I enable authentication in the web UI

You can do this either by setting the appropriate fields in `settings.json` which is
found in TRANSMISSION_HOME which defaults to `/config/transmission-home` so it will be available
on your host where you mount the `/config` volume. Remember that Transmission overwrites the config
when it shuts down, so do this when the container is not running.

Or you can set it using the convenience environment variables. They will then override the settings
on every container startup. The environment variables you have to set are:

* `TRANSMISSION_RPC_USERNAME=username`
* `TRANSMISSION_RPC_PASSWORD=password`
* `TRANSMISSION_RPC_AUTHENTICATION_REQUIRED=true`

PS: Be cautious of special characters in the username or password. We've had multiple errors with
that and have not provided a fix yet. Escaping special characters could be an option, but the
easiest solution is just to avoid them. Make the password longer instead ;)
Or write it into `settings.json` manually as first described.
Also, look up differences between how [yaml special characters](https://support.asg.com/mob/mvw/10_0/mv_ag/using_quotes_with_yaml_special_characters.htm) are escaped vs [in docker run](https://github.com/haugene/docker-transmission-openvpn/issues/1767)

Docker secrets are also supported for the rpc credentials. To use a secret, remove both the `TRANSMISSION_RPC_USERNAME` and `TRANSMISSION_RPC_PASSWORD` environment variables and add a secret named `rpc_creds`. The secret file must contain two lines, the first line is the username and the second line is the password. Then just add it as a secret to docker, and it will be picked up automatically. This is why the name of the secret is important.

## How do I verify that my traffic is using VPN

There are many ways of doing this, and I welcome you to add to this list if you have any suggestions.

You can exec into the container and through the shell use `curl` to ask for your public IP. There are
multiple endpoints for this but here are a few suggestions:

* `curl http://ipinfo.io/ip`
* `curl http://ipecho.net/plain`
* `curl icanhazip.com`

Example command: `docker exec <container-name> curl --silent "http://ipinfo.io/ip"`

Or you could use a test torrent service to download a torrent file and then you can get the IP from that tracker.

* https://github.com/cbdevnet/ipmagnet/
* https://torguard.net/checkmytorrentipaddress.php

## RTNETLINK answers: File exists

[TODO](https://github.com/haugene/docker-transmission-openvpn/issues/1558): Conflicting LOCAL_NETWORK values. Short explanation and link to [networking](vpn-networking.md)

## RTNETLINK answers: Invalid argument

This can occur because you have specified an invalid **subnet** or possibly specified an IP Address in CIDR format instead of a subnet. Your LOCAL_NETWORK property must be aimed at a **subnet** and not at an IP Address. 

A valid example would be

     ```
     LOCAL_NETWORK=10.80.0.0/24
     ```

but an invalid target route that would cause this error might be 

     ```
     #Invalid because the subnet for this range would be 10.20.30.0/24
     LOCAL_NETWORK=10.20.30.45/24
     ```

To check your value, you can use a [subnet calculator](https://www.calculator.net/ip-subnet-calculator.html). 
* Enter your IP Address - the portion before the mask, `10.20.30.45` here
* Select the subnet that matches - the `/24` portion here
* Take the Network Address that is returned - `10.20.30.0` in this case 

## TUNSETIFF tun: Operation not permitted

This is usually a question of permissions. Have you set the [NET_ADMIN capabilities](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities) to the container? What if you use `docker run --privileged`, do you still get that error?

This is an error where we haven't got too much information. If the hints above get you nowhere, create an issue.

## Error resolving host address

This error can happen at multiple places in the scripts. The most common is that it happens with `curl` trying to download the latest .ovpn
config bundle for those providers that have an update script, or that OpenVPN throws the error when trying to connect to the VPN server.

The curl error looks something like `curl: (6) Could not resolve host: ...` and OpenVPN says `RESOLVE: Cannot resolve host address: ...`.
Either way, the problem is that your container does not have a valid DNS setup. We have two recommended ways of addressing this.

The first solution is to use the `dns` option offered by Docker. This is available in
[Docker run](https://docs.docker.com/engine/reference/run/#network-settings) as well as
[Docker Compose](https://docs.docker.com/compose/compose-file/#dns). You can add `--dns 8.8.8.8 --dns 8.8.4.4` to use Google DNS servers.
Or add the corresponding block in docker-compose.yml:
```
  dns:
    - 8.8.8.8
    - 8.8.4.4
```
You can of course use any DNS servers you want here. Google servers are popular. So is Cloudflare DNS.

The second approach is to use some environment variables to override `/etc/resolv.conf` in the container.
Using the same DNS servers as in the previous approach you can set:
```
OVERRIDE_DNS_1=8.8.8.8
OVERRIDE_DNS_2=8.8.4.4
```

This will be read by the startup script and it will override the contents of `/etc/resolv.conf` accordingly. You can have one
or more of these servers and they will be sorted alphabetically.

**What is the difference between these solutions?**

A good question as they both seem to override what DNS servers the container should use. However, they are not equal.

The first solution uses the dns flags from Docker. This will mean that we instruct Docker to use these DNS servers for the container,
but the resolv.conf file in the container will still point to the Docker DNS service. Docker might have many reasons for this but one of
them is at least for service discovery. If you're running your container as a part of a larger docker-compose file or custom docker network
and you want to be able to lookup the other containers based on their service names then you need to use the Docker DNS service.
By using the `--dns` flags you should have both control of what DNS servers are used for external requests as well as container DNS lookup.

The second solution is more direct. It rewrites the resolv.conf file so that it no longer refers to the Docker DNS service.
The effect of this is that you lose Docker service discovery from the container (other containers in the same network can still resolve it)
but you have cut out a middleman and potential point of error. I'm not sure why this sometimes is necessary but it has proven to fix
the issue in some cases.

**Note:** By default, once the VPN tunnel is established the container will automatically switch to DNS servers provided by the VPN,
and pin routes for those servers through the tunnel to prevent DNS leaks. If you use `OVERRIDE_DNS`, the VPN-provided DNS will be
skipped and your specified servers will be used instead. This means DNS traffic may not go through the VPN tunnel.

**A possible third option**

If you're facing the OpenVPN error (not curl) then your provider might have config files with IP addresses instead of DNS.
That way your container won't need DNS to do a lookup for the server. Note that the trackers will still need DNS, so this will only
solve the problem for you if it is your local network that in some way is blocking the DNS.


## Container loses connection after some time

For some users, on some platforms, this is an issue. I have not encountered this myself - but there is no doubt that it's recurring.
Why does the container lose connectivity? That we don't know and it could be many different reasons that manifest the same symptoms.
We do however have some possible solutions.

### Set the ping-exit option for OpenVPN and restart-flag in Docker

Most provider configs have a ping-restart option set. So if the tunnel fails, OpenVPN will restart and re-connect. That works well on regular systems.
The problem is that if the container has lost internet connection restarting OpenVPN will not fix anything. What you can do though is to set/override
this option using `OPENVPN_OPTS=--inactive 3600 --ping 10 --ping-exit 60`. This will tell OpenVPN to exit when it cannot ping the server for 1 minute.

When OpenVPN exits, the container will exit. And if you've then set `restart=always` or `restart=unless-stopped` in your Docker config then Docker will
restart the container and that could/should restore connectivity. VPN providers sometimes push options to their clients after they connect. This is visible
in the logs if they do. If they push ping-restart that can override your settings. So you could consider adding `--pull-filter ignore ping` to the options above.

This approach will probably work, especially if you're seeing logs like these from before:
```
Inactivity timeout (--ping-restart), restarting
SIGUSR1[soft,ping-restart] received, process restarting
```

### Use a third-party tool to monitor and restart the container

The container has a health check script that is run periodically. It will report the health status to Docker and the container will show as "unhealthy"
if basic network connectivity is broken. You can write your own script and add it to cron, or you can use a tool like [https://github.com/willfarrell/docker-autoheal](https://github.com/willfarrell/docker-autoheal) to look for and restart unhealthy containers.

This container has the `autoheal` label by default so it is compatible with the [willfarrell/autoheal image](https://hub.docker.com/r/willfarrell/autoheal/)


## Send Username and Password via a file

Depending on your setup, you may not want to send your VPN user/pass via environment variables (the main reason being, it is accessible via docker inspect). If you prefer, there are two methods of avoiding credentials to be set via environment variables:
 - Option 1: Configure the container to use a file. 
 - Option 2: Configure the container to use a `secret`. (Only available for Docker Swarm)

*Procedure for Option 1: Configuring the container to use a file*
1. Create a text file with a username and password in it, each on a separate line.
For this example, we will assume it is located at `./openvpn-credentials.txt`
```
this_is_my_username
this_is_my_password
```
1. Set the Environment Variable for username/password to exactly: `**None**`

1. Mount the file in this exact path: `/config/openvpn-credentials.txt`

Config example:

```
$ docker run --cap-add=NET_ADMIN -d \
              -v /your/storage/path/:/data \
              -v ./openvpn-credentials.txt:/config/openvpn-credentials.txt \
              -e OPENVPN_PROVIDER=PIA \
              -e OPENVPN_CONFIG=france \
              -e OPENVPN_USERNAME=**None** \
              -e OPENVPN_PASSWORD=**None** \
              -e LOCAL_NETWORK=192.168.0.0/16 \
              --log-driver json-file \
              --log-opt max-size=10m \
              -p 9091:9091 \
              haugene/transmission-openvpn
```

The example docker-compose.yml looks like this:

```
version: '3.3'
services:
    transmission-openvpn:
        cap_add:
            - NET_ADMIN
        volumes:
            - '/your/storage/path/:/data'
            - './openvpn-credentials.txt:/config/openvpn-credentials.txt'
        environment:
            - OPENVPN_PROVIDER=PIA
            - OPENVPN_CONFIG=france
            - OPENVPN_USERNAME=**None**
            - OPENVPN_PASSWORD=**None**
            - LOCAL_NETWORK=192.168.0.0/16
        logging:
            driver: json-file
            options:
                max-size: 10m
        ports:
            - '9091:9091'
        image: haugene/transmission-openvpn
```

*Procedure for Option 2: Configure the container to use a `secret`*

Note: [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/) are only available for Docker Swarm (`docker-compose`). If you run the container standalone, refer to Option 1.

1. Create a text file with a username and password in it, each on a separate line.
For this example, we will assume it is located at `./openvpn-credentials.txt`
```
this_is_my_username
this_is_my_password
```

1. Mount the file as a secret like below. *The name of the secret must be exactly `openvpn_creds`, which will be exposed to the container at `/run/secrets/openvpn_creds`.

The example docker-compose.yml looks like this:

```
version: '3.3'
secrets:
  openvpn_creds:
    file: './openvpn-credentials.txt'
services:
    transmission-openvpn:
        cap_add:
            - NET_ADMIN
        volumes:
            - '/your/storage/path/:/data'
        environment:
            - OPENVPN_PROVIDER=PIA
            - OPENVPN_CONFIG=france
            - OPENVPN_USERNAME=**None**
            - OPENVPN_PASSWORD=**None**
            - LOCAL_NETWORK=192.168.0.0/16
        logging:
            driver: json-file
            options:
                max-size: 10m
        secrets:
            - openvpn_creds
        ports:
            - '9091:9091'
        image: haugene/transmission-openvpn
```

Bonus tip: The same steps can be followed for `rpc_creds`, as shown in [[How do I enable authentication in the web ui](#how_do_i_enable_authentication_in_the_web_UI)].


## AUTH: Received control message: AUTH_FAILED

If your logs end like this, the wrong username/password was sent to your VPN provider.
```
AUTH: Received control message: AUTH_FAILED
SIGTERM[soft,auth-failure] received, process exiting
```

We can divide the possible errors here into three. You have entered the wrong credentials, the server has some kind of error or the container has messed
up your credentials. We have had challenges with special characters. Having "?= as part of your password has tripped up our scripts from time to time.

**NOTE** Some providers have multiple sets of credentials. Some for OpenVPN, others for web login, proxy solutions, etc.
Make sure that you use the ones intended for OpenVPN. **PIA users:** this has recently changed. It used to be a separate pair, but now
you should use the same login as you do in the web control panel. Before you were supposed to use a username like x12345, now it's the p12345 one. There is also a 99 character limit on password length.

First, check that your credentials are correct. Some providers have separate credentials for OpenVPN so it might not be the same as for their apps.
Secondly, test a few different servers just to make sure that it's not just a faulty server. If this doesn't resolve it, it's probably the container.

To verify this you can mount a volume to `/config` in the container. So for example `/temporary/folder:/config`. Your credentials will be written to
`/config/openvpn-credentials.txt` when the container starts, more on that [here](building-blocks.md#starting_openvpn). So by mounting this folder
you will be able to check the contents of that text file. The first line should be your username, the second should be your password.

This file is what's passed to OpenVPN. If your username/password is correct here then you should probably contact your provider.

## Unable to save resume file: Too many open files

Transmission opens one file descriptor per peer (`peer-limit-global`, default 240) plus data,
resume and log files. If the process file-descriptor (`nofile` / `RLIMIT_NOFILE`) soft limit is
too low (the classic default is 1024) you'll see errors like
`Unable to save resume file: Too many open files`.

The container raises this soft limit automatically (up to the container's hard limit) before
starting transmission. You can override the target with the `OPEN_FILES_LIMIT` environment
variable, e.g. `OPEN_FILES_LIMIT=1048576`.

Note that the container only ever changes the **soft** limit, and can only raise it up to the
**hard** limit it was given by the runtime - the hard limit is the ceiling and is never modified.
If you still hit the limit, raise the hard limit on the runtime:

* Docker run: `--ulimit nofile=1048576:1048576`
* docker-compose:
  ```yaml
  ulimits:
    nofile:
      soft: 1048576
      hard: 1048576
  ```
* Kubernetes: there is no native per-pod `nofile` field; set the containerd default
  (`LimitNOFILE` on the containerd systemd unit) on the node. The pod's hard limit is inherited
  from containerd, and the container raises the soft limit to match.

You can verify the limits the running daemon actually has from inside the container:

```
pid=$(pgrep -x transmission-da); grep -i "open files" /proc/${pid:-1}/limits
```

---

### Provider Specific (`docs/provider-specific.md`)

## COMING SOON

**NOTE:** This page is just moved from it's previous location. A re-write is coming.
I'm [on it (#1558)](https://github.com/haugene/docker-transmission-openvpn/issues/1558)

### NORDVPN

The update script is based on the NordVPN API. The API sends back the best recommended OpenVPN configuration file based on the filters given.

You have to use your service credentials instead of your regular email and password. They can be found [here](https://my.nordaccount.com/dashboard/nordvpn/manual-configuration/).

Available ENV variables in the container to define via the NordVPN API the file to use are:

| Variable           | Function                                                                                                                                                            | Example                       |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `NORDVPN_COUNTRY`  | Two character country code. See [/servers/countries](https://api.nordvpn.com/v1/servers/countries) for full list.                                                   | `NORDVPN_COUNTRY=US`          |
| `NORDVPN_CATEGORY` | Server type (P2P, Standard, etc). See [/servers/groups](https://api.nordvpn.com/v1/servers/groups) for full list. Use either `title` or `identifier` from the list. | `NORDVPN_CATEGORY=legacy_p2p` |
| `NORDVPN_PROTOCOL` | Either `tcp` or `udp`. (values identifier more available at https://api.nordvpn.com/v1/technologies, may need script adaptation)                                    | `NORDVPN_PROTOCOL=tcp`        |
| `NORDVPN_SERVER` | Set VPN server FQDN to use, bypasses API recommendations and downloads server's config file. | NORDVPN_SERVER= sg460.nordvpn.com|

The file is then downloaded using the API to find the best server according to the variables, here an albanian, using tcp:

* selecting server (limit answer to 1): [ANSWER]= https://api.nordvpn.com/v1/servers/recommendations?filters[country_id]=2&filters[servers_technologies][identifier]=openvpn_tcp&filters[servers_group][identifier]=legacy_group_category&limit=1
* download selected server's config: https://downloads.nordcdn.com/configs/files/ovpn_[NORDVPN_PROTOCOL]/servers/[ANSWER.0.HOSTNAME][] => https://downloads.nordcdn.com/configs/files/ovpn_tcp/servers/al9.nordvpn.com.tcp.ovpn

One optional ENV var NORDVPN_TESTS can take value from 1 to 4. Expected generic results are written to logs.

| NORDVPN_TESTS | Comment | 
| --------------------- | --------------------- | 
| 1 | Test when nothing is set: All NORDVPN_{COUNTRY, PROTOCOL, CATEGORY} are not set |
| 2 | Test when category is not set: NORDVPN_{COUNTRY, PROTOCOL} are set, NORDVPN_CATEGORY is not set  |
| 3 | Test when api returns no result, send a warning with current parameters.  |
| 4 | Test when NORDVPN_SERVER is set, config file should be downloaded.

get list of servers and load:
`curl --silent https://api.nordvpn.com/server/stats | jq '. | to_entries|sort_by(.value.percent) | "\(.[].key): \(.[].value.percent)"'`

get load of a specific server:
`curl --silent https://api.nordvpn.com/server/stats/ca1509.nordvpn.com | jq '.percent'`

get list of available servers: `curl --silent https://api.nordvpn.com/server/stats | jq '. |to_entries | .[].key')`

### OVPN

OVPN is an external provider. Configs (and a small selection script) are fetched from the [contrib repo](https://github.com/haugene/vpn-configs-contrib/tree/main/openvpn/ovpn) on startup.

Config selection precedence:

1. **`OPENVPN_CONFIG`** — use a specific file by basename (without `.ovpn`), e.g. `OPENVPN_CONFIG=standard.us.chicago.udp`
2. **`OVPN_*` convenience vars** — when `OPENVPN_CONFIG` is unset, these build the same filename as `{connection}.{country}.{city}.{protocol}`
3. **Default** — if neither is set, `default.ovpn` is used (a standard UDP endpoint)

| Variable           | Function                                                                                                                                                            | Example                       |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `OPENVPN_CONFIG` | Direct config basename (without `.ovpn`). Takes precedence over `OVPN_*`. | `OPENVPN_CONFIG=standard.us.chicago.udp` |
| `OVPN_PROTOCOL`  | TCP or UDP                                                                                          | `OVPN_PROTOCOL=udp`          |
| `OVPN_COUNTRY` | Country code | `OVPN_COUNTRY=us` |
| `OVPN_CITY` | City slug | `OVPN_CITY=chicago` |
| `OVPN_CONNECTION` | `standard` or `multihop` | `OVPN_CONNECTION=multihop`        |

As of August 29, 2022, the following options are available (both TCP and UDP exist for most endpoints):
| Type          | Options                                                                                                                                                            | Example                       |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `multihop`  | toronto (ca), zurich (ch), chicago (us), new-york (us), any-city (se)         | `OVPN_COUNTRY=ca   OVPN_CITY=toronto`          |
| `standard` | vienna (at), sydney (au), toronto (ca) , zurich (ch), erfurt (de), frankfurt (de), offenbach (de), copenhagen (dk), madrid (es), helsinki (fl), paris (fr), london (gb), milan (it), tokyo (jp), oslo (no), warsaw (pl), bucharest (ro), gothenburg (se), malmo (se), stockholm (se), sundsvall (se), singapore (sg), kyiv (ua), atlanta (us), los-angeles (us), miami (us), new-york (us), any-city (de), any-city (se), any-city (us) | `OVPN_COUNTRY=us   OVPN_CITY=new-york` |


Review https://github.com/haugene/vpn-configs-contrib/tree/main/openvpn/ovpn for updates to country and city options.  


### MULLVAD & OVPN

According to [(#1355)](https://github.com/haugene/docker-transmission-openvpn/issues/1355)
ipv6 needs to be enabled for mullvad vpn
this is an example for docker compose
```yaml
# ipv6 must be enabled for Mullvad to work
        sysctls:
            - "net.ipv6.conf.all.disable_ipv6=0"
```
or add following line to docker run
```yaml
--sysctl net.ipv6.conf.all.disable_ipv6=0
```

The same is true for provider OVPN.

### NJAL.LA

[Njal.la](https://njal.la/vpn/) provides `.ovpn` configuration file. User
needs to specify to enable ipv6.

Here is a full example of `docker-compose.yml` file, assuming configuration file named `Njalla-VPN.ovpn`
is under local `config` subdirectory.

```yaml
version: '3.3'
services:
    transmission-openvpn:
      cap_add:
        - NET_ADMIN
      volumes:
        - ./config/Njalla-VPN.ovpn:/etc/openvpn/custom/default.ovpn:rw
        - ./data:/data:rw
      dns:
        - 1.1.1.1
      devices:
        - /dev/net/tun
      sysctls:
        # must enable ipv6 to have njal.la work
        - net.ipv6.conf.all.disable_ipv6=0
      environment:
        - OPENVPN_PROVIDER=CUSTOM
        - OPENVPN_USERNAME=user
        - OPENVPN_PASSWORD=pass
        - LOCAL_NETWORK=192.168.1.0/24
        - HEALTH_CHECK_HOST=google.com
      ports:
         - '9091:9091'
      logging:
        driver: json-file
        options:
          max-size: 10m
      image: haugene/transmission-openvpn:latest
```

### PROTONVPN

[PROTONVPN](https://protonvpn.com/support/linux-openvpn/#preparation) provides `.ovpn` configuration files. Just download the one you want to connect with and which allows P2P.

### Prerequisites:
User needs to have a paid account.

1. download your ProtonVPN ovpn file from a destination which allows P2P.
2. in the directory with your docker-compose file, create a directory: `mkdir protonvpn`
3. copy your ovpn file (node-<country of choice>.protonvpn.udp.ovpn) from step 1 to the protonvpn directory
4. add the environment vars below and add +pmp to your username if you want to use port forwarding.
5. add the [update-port.sh](https://github.com/haugene/vpn-configs-contrib/blob/main/openvpn/protonvpn/update-port.sh) script for ProtonVPN from vpn-configs-contrib to the protonvpn directory of step 2.

Here is a full example of `docker-compose.yml` file, assuming configuration file named `node-<country of choice>.protonvpn.udp`
is under local `protonvpn` subdirectory.

```yaml
version: 3.7.1
services:
    transmission-openvpn:
        container_name: TransmissionVPN
        restart: on-failure:2
        cap_add:
            - NET_ADMIN
        volumes:
            - ./protonvpn/:/etc/openvpn/custom/
            - /your/config/path/:/config # where transmission-home is stored
            - /your/storage/path/:/data # where transmission will store the data
        environment:
            - OPENVPN_PROVIDER=custom
            - OPENVPN_CONFIG=node-<country of choice>.protonvpn.udp
            - OPENVPN_USERNAME=<username>+pmp
            - OPENVPN_PASSWORD=<password>
            - LOCAL_NETWORK=192.168.0.0/16
        logging:
            driver: json-file
            options:
                max-size: 10m
        ports:
            - 9091:9091
        image: haugene/transmission-openvpn

```


After starting your container, the `peer listening port` in Transmission should be open after a minute or so. 

If not you can jump in the container and run the script manually and see which error you get, or set the debug env variable: `- DEBUG=true` and look in the logging of your container for the output of the script `update-port.sh`


To check which IP address your VPN is currently connected to, run this script:
```bash
#!/bin/bash

f_container_name()
{
docker ps --format "{{.Names}}"| grep -i transmission
}

f_find_all()
{
curl --silent ipinfo.io/$ext_ip
}

var_cont_name=$(f_container_name)
ext_ip=$(docker exec $var_cont_name curl --silent "http://ipinfo.io/ip")
echo "Transmission VPN currently connected to IP address: $ext_ip"
echo "This IP address is in the following country: "
f_find_all
```

---

### Rss Plugin (`docs/rss-plugin.md`)

The Transmission RSS plugin can optionally be run as a separate container. It allows downloading torrents based on an RSS URL, see [Plugin page](https://github.com/nning/transmission-rss).

```
$ docker run -d \
      -e "RSS_URL=<URL>" \
      --link <transmission-container>:transmission \
      --name "transmission-rss" \
      haugene/transmission-rss
```
At first start a transmission-rss.conf file will be created in /etc if no manual one is mounted
A manual transmission-rss.conf file can be mounted into the container to add additional parameters, e.g. login details to rpc
example:
```
$ docker run -d \
      -v <transmission-rss.conf>:/etc/transmission-rss.conf \
      --link <transmission-container>:transmission \
      --name "transmission-rss" \
      haugene/transmission-rss
```

transmission-rss.conf example

```
feeds:
  - url: <placeholder>
    download_path: <placeholder>
    regexp: <placeholder>
    
server:
  host: transmission
  port: 9091
  rpc_path: /transmission/rpc

login:
  username: <username>
  password: <password>

```

---

### Run Container (`docs/run-container.md`)

# Running the container

Many platforms ship with a Docker runtime and have their own way of setting this up.
I'm then thinking about NAS servers specifically, but also Unraid and others. In addition to this, we have
container management solutions like [Portainer](https://www.portainer.io/)

This page will only discuss the tooling that a Docker installation comes with. That means `docker run ..`
and `docker-compose`. In the end, that is what the other managers do as well and it's the common ground here.
I'm very happy to set up a platform-specific installation page and link to it from here.
Open an issue or PR if you want to contribute with documentation for your favourite platform.


The images available on the Docker Hub are multi-arch manifests. This means that they point to multiple images
that are built for different CPU architectures like ARM for Raspberry Pi. You can `haugene/transmission-openvpn`
on any of these architectures and Docker will get the correct one.

## Starting the container

The example Docker run command looks like this:

```
$ docker run --cap-add=NET_ADMIN -d \
              -v /your/storage/path/:/data \
              -v /your/config/path/:/config \
              -e OPENVPN_PROVIDER=PIA \
              -e OPENVPN_CONFIG=france \
              -e OPENVPN_USERNAME=user \
              -e OPENVPN_PASSWORD=pass \
              -e LOCAL_NETWORK=192.168.0.0/16 \
              --log-driver json-file \
              --log-opt max-size=10m \
              -p 9091:9091 \
              haugene/transmission-openvpn
```

The example docker-compose.yml looks like this:

```
version: '3.3'
services:
    transmission-openvpn:
        cap_add:
            - NET_ADMIN
        volumes:
            - '/your/storage/path/:/data'
            - '/your/config/path/:/config'
        environment:
            - OPENVPN_PROVIDER=PIA
            - OPENVPN_CONFIG=france
            - OPENVPN_USERNAME=user
            - OPENVPN_PASSWORD=pass
            - LOCAL_NETWORK=192.168.0.0/16
        logging:
            driver: json-file
            options:
                max-size: 10m
        ports:
            - '9091:9091'
        image: haugene/transmission-openvpn
```

These configs are equivalent. Running `docker-compose up` with that compose file will result in
the same options being sent to the Docker engine as the run statement before it.

## Three things to remember

#### 1. The container assumes that you mount a folder to /data

Technically you don't have to do this, but it is by far the most manageable way of getting
the downloaded files onto your host system **and** Transmission will store its state there.
So if you don't mount this directory then you will lose all your torrents on image updates.

#### 2. It is not mandatory, but setting OPENVPN_CONFIG is good

If you don't set this then there should be a default config for each provider that is chosen, and that should work fine.
The benefit of choosing yourself is that you can choose a region that is closer to you and that might be better for speed.
I also believe that tinkering with this builds some familiarity with the image and some confidence and understanding for future debugging.

We're now moving towards a setup where we download the configs for our providers when the container starts.
That is great from a maintenance perspective, but it also means that we don't know the valid choices for the providers ahead of time.
A tip for finding out is to set `OPENVPN_CONFIG=dummy` and start it. This will fail, but in the logs, it will print all the valid options.

Pro tip: choose multiple servers. For example: `OPENVPN_CONFIG=france,sweden,austria,italy,belgium`
This will ensure a location near you, but at the same time, it will allow some redundancy. Set Docker to restart the container
automatically and you have a failover mechanism. The container chooses one of the configs at random when it starts and it will bounce
from server to server until it finds one that works.

#### 3. You might not be able to access the Web UI on the first try

The `LOCAL_NETWORK=192.168.0.0/16` tries to fix this for you, but it might not work if your local LAN DHCP server hands out addresses outside that range.

If your local network is in the `10.x.y.z` space for example then you need to set `LOCAL_NETWORK=10.x.0.0/16` or `LOCAL_NETWORK=10.x.y.0/24`.
These are called CIDR addresses and you can read up on them. The short story is that /24 will allow for any value in the last digit place
while /16 will allow any value in the two last places. Be sure to only allow IPs that are in the [private IP ranges](https://en.wikipedia.org/wiki/Private_network).
This option punches a hole in the VPN for the IPs that you specify. It is necessary to reach your Web UI but narrower ranges are better than wide ones.

With that said. If you know that you're on a "typical" network with your router at 192.168.1.1, then `LOCAL_NETWORK=192.168.1.0/24` is better than `LOCAL_NETWORK=192.168.0.0/16`. That way you only allow access from 192.168.1.x instead of 192.168.x.y.

There is an alternative to the LOCAL_NETWORK environment variable, and that is a reverse proxy in the same docker network as the VPN container.
Because this topic is both quite complex and very important there is a separate page on [VPN and Networking](vpn-networking.md) in the container and it goes into depth on why this is.

---

### Supported Providers (`docs/supported-providers.md`)

## How we manage VPN providers

The container used to come bundled with a bunch of config files for a range of VPN providers.
This was fine when it was a handful or even a dozen supported providers, but as we approached
50 providers and 10k configs there wasn't time for anything else than keeping them up to date.

So we've tried to come up with a more maintainable setup. 
We have split the .ovpn configs out to a separate repository at:
[https://github.com/haugene/vpn-configs-contrib](https://github.com/haugene/vpn-configs-contrib).

All static configs that have to be manually updated will live there and be pulled on container startup.
We will try to set up a CODEOWNERS scheme and ask for more help from the community to keep them up to date.

Some providers are still provided from the core project and those are the ones that have implemented
a script for fetching the configs dynamically. Going forward we will allow code in this project, not config.

So that is the story of how we now have two types of providers: `internal` and `external`.
The benefit of having native support for external configs is that it is much simpler for a user to
make a fork of the config repo and simply tell the container to use his or her fork. This way we can hopefully
empower many more to help out with keeping our providers up to date and adding new ones.

## Out-of-the-box supported providers

If you can't find your provider you are welcome to head over to the 
[config repo](https://github.com/haugene/vpn-configs-contrib) to request it or add it yourself.
Keep in mind that some providers generate configs per user where the authentication details are a part
of the config and they can therefore not be added here but has to be manually supplied by the user.
You can use any OpenVPN config with this container by mounting it as a file in the container.
For more info on that see the [using a custom provider](#using_a_custom_provider) section.

### Internal Providers

These providers are implemented as a script in this project and will automatically
download new configs directly from the provider on container startup.

| Provider Name             | Config Value (`OPENVPN_PROVIDER`) |
| :------------------------ | :-------------------------------- |
| IPVanish                  | `IPVANISH`                        |
| NordVPN                   | `NORDVPN`                         |
| Private Internet Access   | `PIA`                             |
| VyprVpn                   | `VYPRVPN`                         |

### External Providers

These providers are fetched from our [config repo](https://github.com/haugene/vpn-configs-contrib) on startup.
They have to be manually updated in that repo when the provider changes them but we're trying to keep them up to date.

Note that we try to keep this list in sync but it is the files and folders in the config repo that ultimately
is the most up-to-date list of configs and providers that are supported.


| Provider Name             | Config Value (`OPENVPN_PROVIDER`) |
| :------------------------ | :-------------------------------- |
| Anonine                   | `ANONINE`                         |
| AnonVPN                   | `ANONVPN`                         |
| BlackVPN                  | `BLACKVPN`                        |
| BTGuard                   | `BTGUARD`                         |
| Cryptostorm               | `CRYPTOSTORM`                     |
| ExpressVPN                | `EXPRESSVPN`                      |
| FastestVPN                | `FASTESTVPN`                      |
| FreeVPN                   | `FREEVPN`                         |
| FrootVPN                  | `FROOT`                           |
| FrostVPN                  | `FROSTVPN`                        |
| Getflix                   | `GETFLIX`                         |
| GhostPath                 | `GHOSTPATH`                       |
| Giganews                  | `GIGANEWS`                        |
| HideMe                    | `HIDEME`                          |
| HideMyAss                 | `HIDEMYASS`                       |
| IntegrityVPN              | `INTEGRITYVPN`                    |
| IronSocket                | `IRONSOCKET`                      |
| Ivacy                     | `IVACY`                           |
| IVPN                      | `IVPN`                            |
| OctaneVPN                 | `OCTANEVPN`                       |
| OVPN                      | `OVPN`                            |
| Privado                   | `PRIVADO`                         |
| PrivateVPN                | `PRIVATEVPN`                      |
| ProtonVPN                 | `PROTONVPN`                       |
| proXPN                    | `PROXPN`                          |
| PureVPN                   | `PUREVPN`                         |
| RA4W VPN                  | `RA4W`                            |
| SaferVPN                  | `SAFERVPN`                        |
| SlickVPN                  | `SLICKVPN`                        |
| SlickVPNCore              | `SLICKVPNCORE`                    |
| Smart DNS Proxy           | `SMARTDNSPROXY`                   |
| SmartVPN                  | `SMARTVPN`                        |
| Surfshark                 | `SURFSHARK`                       |
| TigerVPN                  | `TIGER`                           |
| TorGuard                  | `TORGUARD`                        |
| Trust.Zone                | `TRUSTZONE`                       |
| TunnelBear                | `TUNNELBEAR`                      |
| VPN.AC                    | `VPNAC`                           |
| VPNArea.com               | `VPNAREA`                         |
| VPNBook.com               | `VPNBOOK`                         |
| VPNFacile                 | `VPNFACILE`                       |
| VPN.ht                    | `VPNHT`                           |
| VPNTunnel                 | `VPNTUNNEL`                       |
| VPNUnlimited              | `VPNUNLIMITED`                    |
| Windscribe                | `WINDSCRIBE`                      |
| ZoogVPN                   | `ZOOGVPN`                         |

## Use your own config without building the image

If you have a .ovpn file from your VPN provider and you want to use it but you either don't
know how to build the image yourself or if you don't want to there is another way.

Check out the [guide for this](https://github.com/haugene/vpn-configs-contrib/blob/main/CONTRIBUTING.md)
in the config repo.

## Using a local single .ovpn file from a provider
For some providers, like AirVPN, the .ovpn files are generated per user and contain credentials. 
These files can not be hosted anywhere publicly visible. Then you can mount the files into the container
and use them directly from your local host.

**Grab all files from your provider** (usually a .zip file to download & unzip)

**Copy them into a folder on your Docker host**, there might be .ovpn files and ca.cert as well (example below /volume1/docker/ipvanish/)

**Mount the volume**
Compose sample:
```
             - /volume1/docker/ipvanish/:/etc/openvpn/custom/
```
**Declare the Custom provider, the target server and login/password**
Also important to note here is that `OPENVPN_CONFIG` value needs to be the name of the ovpn file wanting to be referenced in the `/etc/openvpn/custom` volume. In the example below the ovpn file name is `ipvanish-UK-Maidenhead-lhr-c02.ovpn` 

Compose sample:
```
            - OPENVPN_PROVIDER=custom
            - OPENVPN_CONFIG=ipvanish-UK-Maidenhead-lhr-c02
            - OPENVPN_USERNAME=user
            - OPENVPN_PASSWORD=pass
```
Docker ENV vars sample: 
```
              -e OPENVPN_PROVIDER=custom \
              -e OPENVPN_CONFIG=ipvanish-UK-Maidenhead-lhr-c02 \
              -e OPENVPN_USERNAME=user \
              -e OPENVPN_PASSWORD=pass \
```

### Do not mount single config file

Do not mount a single config directly. The container will fail if you try, since it causes sed errors when modify-openvpn-config.sh is executed.
Instead mount the directory where the config exists.

```bash
sed: cannot rename /etc/openvpn/custom/sedHeF3gS: Device or resource busy
```

## Using custom OpenVPN config bundle for supported providers

Many of the VPN provider integrations download a ZIP bundle of the OpenVPN configs from the provider when the container starts. You may want to provide your own config bundle ZIP as opposed to having the container download it. This may be useful if the download URL for the config bundle is blocked on your network.

Currently, this is only supported for Private Internet Access. You can enable this by setting the appropriate env variable (PIA_CUSTOM_BUNDLE) to the location of the zip on the container, and then you must mount the file into the Docker container.

Docker ENV vars sample:
```
              -v /localMachine/pia-config.zip:/etc/openvpn/pia_custom_bundle.zip:ro \
              -e PIA_CUSTOM_BUNDLE=/etc/openvpn/pia_custom_bundle.zip \
```

Compose sample:
```
              environment:
                - OPENVPN_PROVIDER=pia
                - PIA_CUSTOM_BUNDLE=/etc/openvpn/pia_custom_bundle.zip
              volumes:
                - /localMachine/pia-config.zip:/etc/openvpn/pia_custom_bundle.zip:ro
```

To be clear, the ZIP should contain all the PIA OVPN files at the top-level. For example, if you set OPENVPN_CONFIG to uk_london, uk_london.ovpn should exist in the zip.

---

### Tips Tricks (`docs/tips-tricks.md`)

# Use Google DNS servers
Some have encountered problems with DNS resolving inside the docker container.
This causes trouble because OpenVPN will not be able to resolve the host to connect to.
If you have this problem use Docker's --dns flag and try using Google's DNS servers by
adding --dns 8.8.8.8 --dns 8.8.4.4 as parameters to the usual run command.

# Restart the container if the connection is lost
If the VPN connection fails or the container for any other reason loses connectivity, you want it to recover from it. One way of doing this is to set the environment variable `OPENVPN_OPTS=--inactive 3600 --ping 10 --ping-exit 60` and use the --restart=always flag when starting the container. This way OpenVPN will exit if ping fails over a period of time which will stop the container and then the Docker daemon will restart it.

# Let other containers use the VPN

To let other containers use VPN you have to add them to the same Service network as your VPN container runs, you can do this by adding `network_mode: "service:transmission-openvpn"`. Additionally, you have to set `depends_on` to the `transmission-openvpn` service to let docker-compose know that your new container should start **after** `transmission-openvpn` is up and running. As the final step, you can add `healthcheck` to your service.

## Example (Jackett):
As an example, let's add [Jackett](https://github.com/linuxserver/docker-jackett) to the `transmission-openvpn` network based on the example from [Running the container](run-container.md):

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Check if the container is using VPN

After the container starts, simply call `curl` under it to check your IP address, for example with Jackett you should see your VPN IP address as output:

```bash
docker exec jackett curl -s https://api.ipify.org
```

You can also check that Jackett is attached to the VPN network by pinging it from the `transmission-openvpn` Container `localhost`:

```bash
docker exec transmission-vpn curl -Is http://localhost:9117
HTTP/1.1 301 Moved Permanently
Date: Tue, 17 May 2022 19:58:19 GMT
Server: Kestrel
Location: /UI/Dashboard
```

## Example (Dante):
As an example, let's add [Dante](https://www.inet.no/dante/) socks5 proxy to the `transmission-openvpn` network based on the example from [Running the container](run-container.md):

```yaml
version: '3.3'
services:
    transmission-openvpn:
        cap_add:
            - NET_ADMIN
        volumes:
            - '/your/storage/path/:/data'
        environment:
            - OPENVPN_PROVIDER=PIA
            - OPENVPN_CONFIG=france
            - OPENVPN_USERNAME=user
            - OPENVPN_PASSWORD=pass
            - LOCAL_NETWORK=192.168.0.0/16
        logging:
            driver: json-file
            options:
                max-size: 10m
        ports:
            - '9091:9091'
            - '1080:1080'  # This is Dante Socks5 Port – managed by VPN Service Network
        restart: unless-stopped
        image: haugene/transmission-openvpn

    socks5-proxy:
        image: wernight/dante
        restart: unless-stopped
        network_mode: service:transmission-openvpn
        depends_on:
            - transmission-openvpn
        command:
            - /bin/sh
            - -c
            - |
                echo "Waiting for VPN to connect . . ."
                while ! ip link show tun0 >/dev/null 2>&1 || ! ip link show tun0 | grep -q "UP"; do sleep 1; done
                echo "VPN connected. Starting proxy service . . ."
                sed -i 's/^\(external:\).*/\1 tun0/' /etc/sockd.conf
                sockd
```

### Test Dante socks5 proxy
```bash
curl -x socks5h://{docker-host-ip}:1080 http://ip.ip-check.net
```

### Bonus socks5 tip
With the [Proxy SwitchyOmega](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif) Chrome/Edge extension, you can configure specific websites on your local machine to route through your socks5 proxy server.

# Reach sleep or hibernation on your host if no torrents are active
By default, Transmission will always [scrape](https://en.wikipedia.org/wiki/Tracker_scrape) trackers, even if all torrents have completed their activities, or they have been paused manually. This will cause Transmission to be always active, therefore never allow your host server to be inactive and go to sleep/hibernation/whatever. If this is something you want, you can add the following variable when creating the container. It will turn off a hidden setting in Transmission which will stop the application to scrape trackers for paused torrents. Transmission will become inactive, and your host will reach the desired state.
```bash
-e "TRANSMISSION_SCRAPE_PAUSED_TORRENTS_ENABLED=false"
```

# Running it on a NAS
Several popular NAS platforms support Docker containers. You should be able to set up
and configure this container using their web interfaces. As of version 3.0 of this image
creates a TUN interface inside the container by default. This previously had to be mounted
from the host which was an issue for some NAS servers. The assumption is that this should
now be fixed. If you have issues and the logs seem to blame "/dev/net/tun" in some way
then you might consider trying to mount a host device and see if that works better.
Setting up a TUN device is probably easiest to accomplish by installing an OpenVPN package
for the NAS. This should set up the device and you can mount it.
There are some issues involved in running it on Synology NAS, 
Please see this issue that discusses [solutions](https://github.com/haugene/docker-transmission-openvpn/issues/1542#issuecomment-793605649)

# Systemd Integration
On many modern Linux systems, including Ubuntu, systemd can be used to start the transmission-openvpn at boot time, and restart it after any failure.

Save the following as `/etc/systemd/system/transmission-openvpn.service`, and replace the OpenVPN PROVIDER/USERNAME/PASSWORD directives with your settings, and add any other directives that you're using.

This service is assuming that there is a `bittorrent` user set up with a home directory at `/home/bittorrent/`. The data directory will be mounted at `/home/bittorrent/data/`. This can be changed to whichever user and location you're using.

OpenVPN is set to exit if there is a connection failure. OpenVPN exiting triggers the container to also exit, and then the `Restart=always` definition in the `transmission-openvpn.service` file tells systems to restart things again.

```bash
[Unit]
Description=haugene/transmission-openvpn docker container
After=docker.service
Requires=docker.service

[Service]
User=bittorrent
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill transmission-openvpn
ExecStartPre=-/usr/bin/docker rm transmission-openvpn
ExecStartPre=/usr/bin/docker pull haugene/transmission-openvpn
ExecStart=/usr/bin/docker run \
        --name transmission-openvpn \
        --cap-add=NET_ADMIN \
        -v /home/bittorrent/data/:/data \
        -e "OPENVPN_PROVIDER=TORGUARD" \
        -e "OPENVPN_USERNAME=bittorrent@example.com" \
        -e "OPENVPN_PASSWORD=hunter2" \
        -e "OPENVPN_CONFIG=CA Toronto" \
        -e "OPENVPN_OPTS=--inactive 3600 --ping 10 --ping-exit 60" \
        -p 9091:9091 \
        --dns 8.8.8.8 \
        --dns 8.8.4.4 \
        haugene/transmission-openvpn
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

Then enable and start the new service with:

```bash
$ sudo systemctl enable /etc/systemd/system/transmission-openvpn.service
$ sudo systemctl restart transmission-openvpn.service
```

If it is stopped or killed in any fashion, systemd will restart the container. If you do want to shut it down, then run the following command and it will stay down until you restart it.

```bash
$ sudo systemctl stop transmission-openvpn.service
# Later ...
$ sudo systemctl start transmission-openvpn.service
```
# Running with Traefik reverse proxy

A working example of running this container behind a traefik reverse proxy can be found here:
[Config](https://github.com/haugene/docker-transmission-openvpn/issues/1763#issuecomment-844404143)

# Running this container with Podman

The `podman run` command is almost identical to [the one mentioned in README.md](../README.md#docker-run) but with the following exception:

Instead `--cap-add=NET_ADMIN` you have to specify `--cap-add=NET_ADMIN,NET_RAW,MKNOD`. `MKNOD` and `NET_ADMIN` are necessary so Podman can create the tunnel, `NET_RAW` is needed for `ping`.

---

### V3 (`docs/v3.md`)

# Version 3.0 released - we have some breaking changes (but not much)

Those of you who are following this project know that we have had some larger changes coming for a while.
Hobby projects often get last in line for some love and care, and it took longer than I hoped but here we are.

Some highlights on version 3.0:

* We're dropping the ubuntu based image and making alpine the default (reduce double maintenance)
* We're making Transmission settings persistent by default, removing the need for all the environment variables (but keeping support for it)
* We're making it easier to provide your own OpenVPN (.ovpn) config file - adding scripts in the container to modify provider configs as needed to fit the container setup. (still in early stages at this point)
* We're adding a standardized way to add scripts for doing the necessary setup of a provider. This usually means downloading a .ovpn config bundle, unpacking it and modifying it correctly to work in this container.

Hopefully, these changes will improve the usability of this container. As maintainers, we also hope that it will free up time to keep the container up-to-date and stable instead of managing thousands of .ovpn files coming and going.

I'll try to keep a list of breaking changes here, and add to it if we come across more:

* The CREATE_TUN_DEVICE variable now defaults to true. Mounting /dev/net/tun will lead to an error message in the logs unless you explicitly set it to false.
* The DOCKER_LOG variable is renamed to LOG_TO_STDOUT
* If Transmission is running but you can't connect to torrents, try deleting the settings.json file (or renaming it to .backup) and restarting.

PS: Now more than ever. We appreciate that you report bugs and issues when you find them. But as there might be more than usual, please make sure you search and look for a similar one before possibly creating a duplicate.
And you can always revert to the latest tag on the 2.x versions which is 2.14. Instead of running with `haugene/transmission-openvpn` simply use `haugene/transmission-openvpn:2.14` instead. We hope that won't be necessary though :)

---

### Vpn Networking (`docs/vpn-networking.md`)

## COMING SOON

**NOTE:** This page is just moved from it's previous location. A re-write is coming and I know that
there are links to this page that promises more than what's here now. I'm [on it (#1558)](https://github.com/haugene/docker-transmission-openvpn/issues/1558)


## Access the WebUI
But what's going on? My http://my-host:9091 isn't responding?
This is because the VPN is active, and since docker is running in a different ip range than your client the response
to your request will be treated as "non-local" traffic and therefore be routed out through the VPN interface.

### How to fix this
The container supports the `LOCAL_NETWORK` environment variable. For instance if your local network uses the IP range 192.168.0.0/24 you would pass `-e LOCAL_NETWORK=192.168.0.0/24`.

Alternatively you can reverse proxy the traffic through another container, as that container would be in the docker range. There is a reverse proxy being built with the container. You can run it using the command below or have a look in the repository proxy folder for inspiration for your own custom proxy.

```
$ docker run -d \
      --link <transmission-container>:transmission \
      -p 8080:8080 \
      --name transmission-openvpn-proxy \
      haugene/transmission-openvpn-proxy
```
## Access the RPC

You need to add a / to the end of the URL to be able to connect. Example: http://my-host:9091/transmission/rpc/

## Controlling Transmission remotely
The container exposes /config as a volume. This is the directory where the supplied transmission and OpenVPN credentials will be stored.
If you have transmission authentication enabled and want scripts in another container to access and
control the transmission-daemon, this can be a handy way to access the credentials.
For example, another container may pause or restrict transmission speeds while the server is streaming video.

---

### Web Proxy (`docs/web-proxy.md`)

### Web proxy configuration options

This container also contains a web-proxy server to allow you to tunnel your web browser traffic through the same OpenVPN tunnel.
The proxy used is [Privoxy](https://www.privoxy.org/) and is highly configurable using the built-in web interface available on [config.privoxy.org](http://config.privoxy.org) (available once your browser is correctly configured to use the localhost:8118 HTTP Proxy).
This is useful if you are using a private tracker that needs to see you log in from the same IP address you are torrenting from.
The default listening port is 8118. Note that only ports above 1024 can be specified as all ports below 1024 are privileged
and would otherwise require root permissions to run.
Remember to add a port binding for your selected (or default) port when starting the container.

| Variable                | Function                | Example                         |
| ----------------------- | ----------------------- | ------------------------------- |
| `WEBPROXY_ENABLED`      | Enables the web proxy   | `WEBPROXY_ENABLED=true`         |
| `WEBPROXY_PORT`         | Sets the listening port | `WEBPROXY_PORT=8118`            |
| `WEBPROXY_BIND_ADDRESS` | Sets the listen address | `WEBPROXY_BIND_ADDRESS=0.0.0.0` |

The listening address is the one found bound to the eth0 interface unless `WEBPROXY_BIND_ADDRESS` is set.

`adr=$(ip -4  a show eth0| grep -oP "(?<=inet )([^/]+)")`

---

--- METRICS ---
- Files Extracted: 14
- Estimated Token Budget: ~24263 tokens
- Canonical Reference: https://codewiki.google/github.com/haugene/docker-transmission-openvpn
