Dockerfile, Caddy, and qBittorrent, oh my!

Moved these here from Electric Eel - How I am using Dockerfile, .env files, compose files

Got this working for all my apps now except one: qBittorrent. That’s using Gluetun for VPN access, which I’m sure is where the problem lies. When I’d added Traefik labels to the qbittorrent (where I currently have the Caddy labels), it worked, but Caddy complains about qbittorrent not being on the proxy network (which of course it isn’t). Thoughts on what I’d need to change here? Compose files below:

services:
  caddy:
    restart: unless-stopped
    init: true
    env_file: .env
    ports:
      - 80:80
      - 443:443/tcp
      - 443:443/udp
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /mnt/software/docker/data/caddy/config:/config
      - /mnt/software/docker/data/caddy/data:/data
      - /mnt/software/docker/data/caddy/html:/usr/share/caddy
    labels:
      - caddy_0.email="{env.EMAIL}"
      - caddy_0.acme_dns=cloudflare
      - caddy_0.acme_dns.api_token="{env.CLOUDFLARE_API_TOKEN}"
      - caddy_1=(encode)
      - caddy_1.encode=zstd gzip
      - caddy_2=localhost:80, localhost
      - caddy_2.root="* /usr/share/caddy"
      - caddy_2.file_server
      - homepage.group=Admin
      - homepage.name=Caddy
      - homepage.description=Caddy Server/Reverse Proxy
      - homepage.icon=caddy
      - homepage.widget.type=caddy
      - homepage.widget.url=http://caddy:2019
    build:
      context: .
    healthcheck:
      test:
        - CMD-SHELL
        - wget --no-verbose -T3 --spider http://localhost:80 || exit 1
      interval: 1m
      timeout: 10s
      retries: 3
      start_period: 30s
      start_interval: 5s
    pull_policy: missing
    networks:
      - proxy
networks:
  proxy:
    external: true
services:
  gluetun:
    image: qmcgaw/gluetun
    container_name: gluetun
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    expose:
      - 6881
      - 6881/udp
    devices:
      - /dev/net/tun:/dev/net/tun
    environment:
      - VPN_SERVICE_PROVIDER=surfshark
      - VPN_TYPE=wireguard
      - WIREGUARD_PRIVATE_KEY=(REMOVED)
      - WIREGUARD_ADDRESSES=(REMOVED)
      - SERVER_HOSTNAMES=(REMOVED)
      - TZ=America/New_York
    networks:
      - proxy
    labels:
      - com.centurylinklabs.watchtower.enable=true
networks:
  proxy:
    external: true
services:
  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - PUID=568
      - PGID=568
      - TZ=America/New_York
      - WEBUI_PORT=8080
      - TORRENTING_PORT=6881
    volumes:
      - /mnt/software/docker/data/qbittorrent/config:/config
      - /mnt/tank/torrents/qbittorrent:/mnt/torrents/qbittorrent
    restart: unless-stopped
    network_mode: container:gluetun
    labels:
      - caddy=qbt.domain
      - caddy.reverse_proxy="{{upstreams 8080}}"
networks: {}

I think it’s easier to set up a VPN on your router. Thus, you can access your entire LAN via VPN. I’ve never managed to get gluetun to work, though.

1 Like

I think it should be https://<a-record>.<domain-name>. You are a cert issuing expert after all :slightly_smiling_face:.

For remote-access VPN, I agree, and have done so. But that doesn’t really address “send this traffic over a VPN, so as to obscure its origin,” which is what I’m trying to do (and have been doing) with qBittorrent.

What’s posted above has worked perfectly for me for several months, with Traefik labels for qBittorrent rather than Caddy ones (and obviously with credentials added for Gluetun).

:pirate_flag:

Well, at first I wanted to answer for this particular case, but then I saw the VPN access thing and changed my mind. Now I changed my mind again.

As I said, I’ve never managed to make gluetun work. Ended up with shadowsocks/shadowsocks-libev:v3.3.5. Its client container is acting like a socks5 proxy, so the other containers can use it with something like:

environment:
      - http_proxy=socks5h://<socks-proxy-ip>
      - https_proxy=socks5h://<socks-proxy-ip>
      - no_proxy=${NO_PROXY:-localhost}

Although, quick googling shown that surfshark doesn’t support shadowsocks.

  • Only a tiny percentage of Surfshark users (0.5%) used the Shadowsocks protocol.

Only a tiny percentage of Excel users are using macros. Let’s drop them for the win! :clown_face: :circus_tent: :clown_face:

1 Like

First off, excellent VPN choice, Gluetun has been an amazing solution for years. While I don’t know much about using a reverse proxy I do recommend making one compose file to rule them all and not specifying the network within the compose file. I’ll share a shortened compose file below and you’ll see a reference for creating a separate network but that is because I need containers behind the VPN to be able to talk to plex (not behind it). This was in the Gluetun docs. I believe the combination of using separate compose files and not specifying your Proxy network on QBT is potentially the cause of your issue. That said, why did it work on Traefik and not Caddy? No idea. Maybe you can take something from my compose and retrofit it to Caddy.

I use this for connecting the VPN to the containers:

network_mode: "service:gluetun"

The only other suggestion I have is to use the below on any containers that need to be behind the VPN. This is like a killswitch if the VPN is not working properly.

    depends_on:
      gluetun:
        condition: service_healthy

Shortened compose file:

services:
  gluetun:
    image: qmcgaw/gluetun
    networks:
      gluetun_network:
    container_name: gluetun
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    ports:
      - 8888:8888/tcp # HTTP proxy
      - 8388:8388/tcp # Shadowsocks
      - 8388:8388/udp # Shadowsocks
      - 8081:8081/tcp
      - 9696:9696/tcp
      - 8080:8080/tcp
    volumes:
      - ./gluetun:/gluetun
      - ./port:/tmp/gluetun/
    environment:
      - VPN_SERVICE_PROVIDER=private internet access
      - VPN_TYPE=openvpn
      - OPENVPN_USER=
      - OPENVPN_PASSWORD=

  plex:
    container_name: plex
    image: plexinc/pms-docker
    restart: unless-stopped
    networks:
      gluetun_network:
        ipv4_address: 172.20.0.22
    volumes:
      - ./plex:/config
      - /home/data/rr/complete:/complete
      - /home/data/sideload:/sideload
    environment:
      - PLEX_UID=3000
      - PLEX_GID=3000
      - TZ=America\Chicago
      - PLEX_CLAIM="
      - ADVERTISE_IP=http://192.168.1.15:32400/
    ports:
      - 32400:32400/TCP
    devices:
      - /dev/dri:/dev/dri

  heimdall:
    image: lscr.io/linuxserver/heimdall:latest
    container_name: heimdall
    restart: unless-stopped
    environment:
      - PUID=3000
      - PGID=3000
      - TZ=America\Chicago
    volumes:
      - ./heimdall:/config
    ports:
      - 80:80

  homeassistant:
    container_name: homeassistant
    restart: unless-stopped
    network_mode: host
    image: ghcr.io/home-assistant/home-assistant:stable
    volumes:
      - ./homeassistant:/config
      - /etc/localtime:/etc/localtime:ro
      - /home/data/rr/datastore/media:/media
      - /run/dbus:/run/dbus:ro
    environment:
      - PUID=3000
      - PGID=3000
      - TZ=America\Chicago

  qbittorrent:
    container_name: qbittorrent
    network_mode: "service:gluetun"
    image: lscr.io/linuxserver/qbittorrent:latest
    environment:
      - PUID=3000
      - PGID=3000
      - TZ=America\Chicago
      - WEBUI_PORT=8081
    volumes:
      - ./qbittorrent:/config
      - /home/data/rr:/rr
    depends_on:
      gluetun:
        condition: service_healthy

networks:
  gluetun_network:
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/16
2 Likes

@dan have a look at this. vpn baked into qbittorrent container. Saved me the hassle dealing with gluetun.

3 Likes

Thanks for the suggestions, @swc-phil, @usergiven, and @mistermanko. I was wanting to keep things as similar as possible to previously, because it’d been working without issue previously. The solution I’ve found was to bring the gluetun container into the qBittorrent stack, like this:

services:
  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - PUID=568
      - PGID=568
      - TZ=America/New_York
      - WEBUI_PORT=8080
      - TORRENTING_PORT=6881
    volumes:
      - /mnt/software/docker/data/qbittorrent/config:/config
      - /mnt/tank/torrents/qbittorrent:/mnt/torrents/qbittorrent
    restart: unless-stopped
    network_mode: service:gluetun
    labels:
      - com.centurylinklabs.watchtower.enable=true
  gluetun:
    image: qmcgaw/gluetun
    container_name: gluetun
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    expose:
      - 6881
      - 6881/udp
    devices:
      - /dev/net/tun:/dev/net/tun
    environment:
      - VPN_SERVICE_PROVIDER=surfshark
      - VPN_TYPE=wireguard
      - WIREGUARD_PRIVATE_KEY=(REMOVED)
      - WIREGUARD_ADDRESSES=(REMOVED)
      - SERVER_HOSTNAMES=(REMOVED)
      - TZ=America/New_York
    networks:
      - proxy
    labels:
      - caddy=qbt.domain
      - caddy.reverse_proxy="{{upstreams 8080}}"
      - homepage.group=Media
      - homepage.name=qBittorrent
      - homepage.icon=qbittorrent
      - homepage.href=https://qbt.domain
      - homepage.description=Bittorrent client
      - homepage.widget.type=qbittorrent
      - homepage.widget.url=https://qbt.domain
      - homepage.widget.username=admin
      - homepage.widget.password=(REMOVED)
      - homepage.widget.enableLeechProgress=false
      - com.centurylinklabs.watchtower.enable=true
networks:
  proxy:
    external: true

This wouldn’t be ideal if other services were depending on the VPN, but this is the only one that is.

It still isn’t clear to me why the previous configuration worked with Traefik (including having the Traefik labels on the qbittorrent container) and not with Caddy, but this nonetheless works.

5 Likes

I started using qbittorrent with gluetun before I migrated to TrueNAS, and your solution above is exactly how I made it work (and the only way I made it work). I combine both containers into the same stack and use
network_mode: service:gluetun
to “glue” them together, as opposed to
network_mode: container:gluetun
I haven’t worked with Caddy yet, but after seeing your recent article on setting it up, I’ll probably tackle this soon. Thanks for showing your work!

1 Like