arch-qbittorrentvpn puts a VPN and a proxy in one container with qBittorrent
Docker build script for Arch Linux base with qBittorrent, Privoxy and OpenVPN
At a glance
- What is it?
- binhex/arch-qbittorrentvpn is a Docker build script that layers qBittorrent, Privoxy, microsocks and OpenVPN or WireGuard on an Arch Linux base image, and configures everything through environment variables at container start. The README is unusually explicit about the leak risks, the privileged mode WireGuard needs, and the ports that will get you in trouble.
- Who is it for?
- Self-hosters who want qBittorrent to run inside a VPN tunnel without hand wiring iptables rules should adopt arch-qbittorrentvpn, because the image ships the leak prevention and the routing configuration that are tedious to get right, and the README documents the failure modes that matter. It is the wrong tool if you cannot run a container with NET_ADMIN, and it is the wrong tool if WireGuard is your protocol, because that path requires a privileged container.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A container whose job is to keep traffic inside the tunnel
arch-qbittorrentvpn is a Docker build script, and the repository is small enough to describe in one line: a Dockerfile, a build directory, a config directory, a run directory and a README.
What the image assembles is qBittorrent, Privoxy, microsocks, OpenVPN and WireGuard on an Arch Linux base. The README's own description of the intent is that the image includes OpenVPN and WireGuard to ensure a secure and private connection to the internet, including the use of iptables to prevent IP leakage when the tunnel is down.
That last clause is the design centre of the project. A container that runs a torrent client next to a VPN client has a failure mode that a normal application does not: the tunnel drops, the container is still running, and the traffic that was supposed to be private now goes out through the host's normal route. Blocking that with iptables inside the container is what this image is for.
The build notes list the sourcing policy, and it is worth knowing because it determines how fresh the image is. qBittorrent, Privoxy, OpenVPN and WireGuard come as the latest stable release from the Arch Linux repository, while microsocks comes as the latest stable release from GitHub. So the image tracks rolling upstream packages rather than pinning versions, which is a deliberate trade between freshness and reproducibility.
The audience is someone running a container on their own hardware or homelab who wants the torrent client, the tunnel and the proxy to live together with predictable routing.
Two layers: a shared VPN base image and a thin application layer
The Dockerfile is short and its structure explains the design.
The first line is the base image, ghcr.io/binhex/arch-int-vpn:latest, and everything else builds on top of that. The base image is a shared foundation from the same author, which means the VPN plumbing, the supervisor setup and the user model are solved once and reused by several application images.
The application layer then does four things. It adds supervisor configuration files from build into /etc/supervisor/conf.d, so that the processes inside the container are started and monitored by supervisord rather than by an entrypoint script. It adds installation shell scripts from build/root into /root. It adds runtime scripts from run/nobody into /home/nobody. And it adds preconfigured configuration files for the nobody user from config/nobody into /home/nobody.
The installation step then runs as a build step, passing three values as arguments:
RUN chmod +x /root/*.sh /home/nobody/*.sh && \
/bin/bash /root/install.sh "${APPNAME}" "${RELEASETAG}" "${TARGETARCH}" "${TARGETARCH}"The app name and release tag come from buildx arguments, and the architecture is passed twice, which is a small detail that says the install script expects to know both the target and the build platform. The result is a container that starts by running a shell script as its command:
CMD ["/bin/bash", "init.sh"]For a user, the practical consequence is that the container's behaviour at startup is driven by scripts and supervisor configuration rather than by a single binary, and that the same VPN base means the OpenVPN and WireGuard handling in this image is the same handling used across the author's other images.
Environment variables are the entire configuration surface
There is no configuration file to edit and no command line flags beyond what the environment carries. The README's usage section is one long docker run template with every setting as an environment variable, and the PIA example below it shows the same list filled in.
-e VPN_ENABLED=<yes|no> \
-e VPN_USER=<vpn username> \
-e VPN_PASS=<vpn password> \
-e VPN_PROV=<pia|airvpn|protonvpn|custom> \
-e VPN_CLIENT=<openvpn|wireguard> \
-e VPN_OPTIONS=<additional openvpn cli options> \
-e ENABLE_STARTUP_SCRIPTS=<yes|no> \
-e ENABLE_PRIVOXY=<yes|no> \
-e STRICT_PORT_FORWARD=<yes|no> \
-e USERSPACE_WIREGUARD=<yes|no> \
-e ENABLE_SOCKS=<yes|no> \The shape of that list tells you the design. The provider is one of four values, so the image has built-in knowledge of PIA, AirVPN and ProtonVPN plus a custom option for anything else. The client is either openvpn or wireguard, and VPN_OPTIONS takes extra OpenVPN command line options for the cases where the built-in profiles do not fit.
There are also switches that turn features off rather than on: ENABLE_STARTUP_SCRIPTS, ENABLE_PRIVOXY, STRICT_PORT_FORWARD, USERSPACE_WIREGUARD and ENABLE_SOCKS all take yes or no. That is a deliberate posture. A container that starts with a SOCKS proxy exposed, or with port forwarding enforced, is a container that leaks traffic by default, and the defaults here are conservative.
The remaining variables cover the operational details: LAN_NETWORK for the local network in CIDR notation, NAME_SERVERS for the resolvers, VPN_INPUT_PORTS and VPN_OUTPUT_PORTS for the port bindings, DEBUG, WEBUI_PORT for the web interface, HEALTHCHECK_COMMAND, HEALTHCHECK_ACTION and HEALTHCHECK_HOSTNAME for the health check, UMASK, PUID and PGID for the file ownership of everything the container writes.
Two more details in the README matter for day two. The web interface password is randomly generated and shown in /config/supervisord.log, and the user id and group id to use can be found by running id for the user you want the container to run as.
The port warning is the most important paragraph in the README
One section of the README is marked IMPORTANT, and it deserves to be read as a specification rather than as advice.
The warning is that VPN_INPUT_PORTS is not to define the incoming port for the VPN. This environment variable is used to define ports you want to allow in to the VPN network when binding multiple containers together, and configuring it incorrectly with the VPN provider assigned incoming port could result in IP leakage.
That is a specific and plausible failure. The port your provider gives you for incoming connections is the one that makes the tunnel bidirectional. If you map it yourself in the container's port list, the container accepts that traffic directly instead of routing it through the tunnel, and the connection is no longer private.
The default docker run template makes the same mistake look attractive, because it maps 58946 twice, once for TCP and once for UDP, which is the shape of a port-forwarded incoming port. Reading the warning next to that line is the point of the section.
Two other configuration notes carry real weight. The README says that due to Google and OpenDNS supporting EDNS Client Subnet it is recommended not to use either of these NS providers, and it lists the default resolvers used in the examples: DNS Watch at 84.200.x.x, FreeDNS at 37.235.x.x and Cloudflare at 1.x.x.x. DNS leak prevention is as much a part of a private setup as the tunnel itself.
The third note is about the web interface port. Due to issues with CSRF and port mapping, if you change the port for the web interface you have to change both sides of the -p switch and set WEBUI_PORT to the same value, for example -p 8090:8090 together with WEBUI_PORT=8090.
WireGuard needs a privileged container, and that changes the risk
The README draws a hard line between the two supported protocols, and the difference is about permissions rather than performance.
For OpenVPN, the container needs NET_ADMIN, which is what the default run template adds with --cap-add=NET_ADMIN.
For WireGuard, the README explains that due to the enhanced security and kernel integration WireGuard will require the container to be defined with privileged permissions and sysctl support. The options change from:
``` bash --cap-add=NET_ADMIN \
to
bash --sysctl="net.ipv4.conf.all.src_valid_mark=1" \ --privileged=true \ ```
A privileged container is a materially different security posture from one with a single added capability. It is not a blocker for a homelab, but it is a decision that should be made deliberately rather than discovered by copying the wrong example.
The configuration files also differ in where they come from. For PIA users the WireGuard configuration file is auto generated after the first run and stored in /config/wireguard/wg0.conf, and the Endpoint line in that file can be edited if you want a different endpoint, with Netherlands as the default. For other providers you download the configuration file from your provider, start and stop the container once to generate the /config/wireguard/ folder, and place your file there.
The OpenVPN path needs the same manual step. The image does not include the OpenVPN configuration file and certificates; those are typically downloaded from the provider's website as a zip containing several ovpn files. You extract them into /config/openvpn/, deleting the ones you do not want and leaving a single ovpn file together with the certificates it references, which normally have a crt or a pem extension. If the folder does not exist, starting and stopping the container forces its creation.
Privoxy and microsocks exist for traffic that must not use the tunnel
Two proxies sit alongside the client, and they exist for different reasons.
Privoxy is there to allow unfiltered access to index sites, and the README says to point the application at http://<host ip>:8118 to use it. In practice that means your client connects to the local proxy, and the proxy reaches the index over a route that is not the tunnel, because index sites are exactly the places where a blocked or logged-in region matters. The proxy is disabled by default through ENABLE_PRIVOXY.
microsocks is a SOCKS5 server on port 9118, reachable at <host ip>:9118. The README states the default credentials plainly: admin/socks. That is worth pausing on, because it is a credential pair published in the documentation of an image whose whole purpose is private access. The port is published in the default run template, so a deployment that keeps the defaults exposes an unauthenticated-in-practice SOCKS proxy to whatever can reach the host. Changing SOCKS_USER and SOCKS_PASS, or not publishing 9118 at all, is the difference between a proxy and an open relay.
The web interface is the third surface. It is at http://<host ip>:8080/ with the username admin and a randomly generated password that appears in /config/supervisord.log, which is convenient for a first run and awkward for automation, since the password has to be read out of a log file rather than supplied as a variable.
Together the three surfaces cover the usual split: the web interface for configuration, Privoxy for index traffic that should bypass the tunnel, and microsocks for applications that need a SOCKS endpoint inside the container.
Where this image is the wrong tool
The limitations are mostly consequences of the container model rather than gaps in the build script.
First, it is Docker only. The project is a Docker build script and the README documents docker run invocations, volumes and capabilities. There is no documented systemd, podman or bare metal path, and if you run a container platform other than Docker you are adapting the instructions yourself.
Second, WireGuard needs a privileged container, which as noted above is a real cost in isolation terms. That is a property of the kernel integration rather than a choice this project could avoid, but it means the OpenVPN path and the WireGuard path do not have the same risk profile and should not be treated as interchangeable.
Third, the credentials model is thin. There is one username and password for the VPN provider and another username and password for the SOCKS server, both passed as environment variables, and the microsocks defaults are documented in the README. Environment variables passed to docker run also end up in the container's configuration and in the process table, so a deployment that treats them as secrets should be aware of that.
Fourth, the image is not a policy appliance. It offers port forwarding switches and DNS resolver choices, and it blocks leaks with iptables, but it does not describe firewall rules for other containers on the same host, and the STRICT_PORT_FORWARD switch is a preference rather than an enforcement policy you can audit from the outside.
On maintenance, the record is good. The repository is not archived and the last push was on 2026-10-20, following a manual release 5.2.4-1-02 on 2026-09-30 and an API or URL triggered release 5.2.4-1-01 the day before that. The mixture of manual and automated release naming tells you the image is built both by hand and by automation, and since the packages come from rolling Arch repositories rather than pinned versions, an image you pull today is not the image you pulled last month. The licence is GPL-3.0, which for a container image you build and run yourself is not a distribution question, but it does mean modifications to the build scripts are shared under the same terms.
Editorial conclusion
Self-hosters who want qBittorrent to run inside a VPN tunnel without hand wiring iptables rules should adopt arch-qbittorrentvpn, because the image ships the leak prevention and the routing configuration that are tedious to get right, and the README documents the failure modes that matter. It is the wrong tool if you cannot run a container with NET_ADMIN, and it is the wrong tool if WireGuard is your protocol, because that path requires a privileged container. Before deploying, read the port mapping section rather than copying the example, change the default microsocks credentials, and check that the OpenVPN or WireGuard configuration files you place in /config match the provider you actually use.
Frequently asked questions
Which VPN providers does arch-qbittorrentvpn support?
The VPN_PROV variable takes one of pia, airvpn, protonvpn or custom, so three providers are built in and a fourth value covers anything else. The OpenVPN and WireGuard configuration files themselves are not included in the image and have to come from your provider.
How do I use WireGuard with this image?
Set VPN_CLIENT to wireguard and change the container options from --cap-add=NET_ADMIN to --sysctl="net.ipv4.conf.all.src_valid_mark=1" with --privileged=true, because WireGuard needs privileged permissions and sysctl support for its kernel integration.
What are the default ports for the web interface and the proxies?
The web interface is on 8080 with the username admin and a randomly generated password shown in /config/supervisord.log, Privoxy is on 8118, and microsocks is on 9118 with the documented default credentials admin/socks.
What does VPN_INPUT_PORTS do, and why is it called out as important?
It defines ports you want to allow in to the VPN network when binding multiple containers together. It is not the incoming port for the VPN, and configuring it with the provider assigned incoming port could result in IP leakage, which is why the README marks the section IMPORTANT.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/binhex-arch-qbittorrentvpn)Was this useful?