A docker-compose I’ve been using on my home server for the past few years.

Overview

There are 2 services:

  • The well-maintained unofficial Misioslav/expressvpn image.
    • The ports exposed are those that the Transmission uses:
      • 9091 to expose Transmission’s web interface.
      • 51413 (both TCP & UDP) to expose Transmission service to peers over DHT.
    • Healthcheck uses the ExpressVPN command status to check if connected.
  • linuxserver.io’s Transmission image linuxserver/transmission.
    • No ports needed to explicitly expose because it uses the above Expressvpn service as its network (which itself exposes Transmission’s ports for it).
    • Healthcheck curls ExpressVPN’s Whats-My-IP page and makes sure it doesn’t include the string Exposed or exposed (which it will for traffic not coming from one of ExpressVPN’s servers).
services:
    expressvpn:
        container_name: expressvpn
        hostname: expressvpn
        image: misioslav/expressvpn:latest
        restart: unless-stopped
        ports: # ports from which container that uses expressvpn connection will be available in local network
        - 9091:9091
        - 51413:51413
        - 51413:51413/udp
        environment:
        - WHITELIST_DNS=192.168.1.1,1.1.1.1,8.8.8.8  # Comma separated list of dns servers you wish to use and whitelist via iptables
        - CODE=<ACTIVATION_CODE_OF_YOUR_EXPRESS_VPN_ACCOUNT> # Activation Code from ExpressVPN https://www.expressvpn.com/support/troubleshooting/find-activation-code/
        - SERVER=smart # By default container will connect to smart location, can specify location like 'spain' - any country lowercase from list: https://www.expressvpn.com/vpn-server#vpn-locations
        cap_add:
        - NET_ADMIN
        devices:
        - /dev/net/tun
        stdin_open: true
        tty: true
        command: /bin/bash
        privileged: true
        healthcheck:
            test:  "expressvpn status | grep 'Connected to'"
            interval: 60s
            timeout: 5s
            retries: 2
            start_period: 10s
    transmission:
        container_name: transmission
        image: linuxserver/transmission:latest
        network_mode: service:expressvpn    
        environment:
        - PUID=1000
        - PGID=1000
        - TZ=<DESIRED TIMEZONE, E.G. 'Etc/EST'>
        volumes:
        - <FULL_PATH_ON_HOST_WHERE_YOU_WANT_TRANSMISSION_CONFIG_STORED_ON_HOST>:/config
        - <FULL_PATH_ON_HOST_WHERE_YOU_WANT_TRANSMISSION_DOWNLOADS_TO_GO>:/downloads
        - <FULL_PATH_ON_HOST_WHERE_YOU_WANT_TRANSMISSION_TO_WATCH_FOR_NEW_TORRENT_FILES_PLACED>:/watch
        restart: unless-stopped
        healthcheck:
            test: "! curl -s https://www.expressvpn.com/what-is-my-ip | grep '[Ex]posed'"
            interval: 60s
            timeout: 5s
            retries: 3
            start_period: 10s
        depends_on:
            expressvpn:
                condition: service_healthy