Skip to content

About

A containerized fork of ipMagnet with SWAG

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

ipMagnetize

ipMagnetize is a fork of ipMagnet, which shows which IP addresses your BitTorrent client hands out to trackers. See upstream for what the app does, its privacy notes, and manual (non-Docker) setup.

This fork adds:

  • A Docker image configured through environment variables, published at ghcr.io/nkboedecker/ipmagnetize for linux/amd64 and linux/arm64.
  • An optional HTTPS setup using SWAG with Let's Encrypt certificates.
  • Correct client IP detection behind a reverse proxy (TRUST_PROXY).

DISCLOSURE: This code was written with the assistance of Claude Code (Anthropic).

Quick start

From a checkout of this repository:

docker compose up -d

This runs the published image (see Image tags). To build the image from your checkout instead, add --build.

ipMagnetize is now on http://localhost/. Before exposing it publicly, set TRACKER_URL to the address BitTorrent clients will reach it at (see Configuration), or the magnet links it generates will point clients at localhost.

Configuration

Settings

Variable Default Set in Purpose
TRACKER_URL http://localhost/ override file Public URL of this instance, with trailing slash. Embedded in magnet links as the tracker.
ENABLE_INTERVAL false override file Ask clients to re-announce periodically. Read upstream's warning before enabling.
TRACKER_INTERVAL 300 override file Re-announce interval in seconds, if enabled.
TRUST_PROXY false override file Take the client IP from a reverse proxy's X-Real-IP header. The SWAG overlay sets this to true. See Behind another reverse proxy.
IPMAGNETIZE_BIND 0.0.0.0 .env Host address ipMagnetize's port is published on.
IPMAGNETIZE_PORT 80 .env Host port ipMagnetize is published on.
IPMAGNETIZE_MEM_LIMIT 256m .env Memory cap for the ipMagnetize container. It idles around 20 MB.
IPMAGNETIZE_VERSION 1 .env Tag of the published image to run. See Image tags.
SWAG_URL (required for SWAG) .env Your registered domain, e.g. example.com.
SWAG_EMAIL (required for SWAG) .env Contact address for Let's Encrypt.
SWAG_TZ Etc/UTC .env Time zone for the SWAG container.
SWAG_CONFIG_DIR ./swag-config .env SWAG's persistent config: certificates, nginx config, and the Cloudflare token.
SWAG_MEM_LIMIT 512m .env Memory cap for the SWAG container. Raise it if SWAG also serves other apps.
SWAG_VERSION 5.8.0-ls486 .env SWAG image tag. See Updating.

If TRACKER_URL contains ", $, \ or #, or TRACKER_INTERVAL isn't a whole number, the container refuses to start. Run docker compose logs ipmagnetize to see why.

Where to put your settings

Don't edit docker-compose.yml. Your changes would conflict with (or be overwritten by) the next git pull. Use these two files instead. Both are ignored by git.

  • docker-compose.override.yml holds the container settings (TRACKER_URL and the others marked "override file" above):

    services:
      ipmagnetize:
        environment:
          TRACKER_URL: "https://ipmagnetize.example.com/"

    A plain docker compose command loads this file automatically. If you list compose files yourself with -f or COMPOSE_FILE, include it in the list.

  • .env holds the variables Compose itself reads (the ones marked .env above), one NAME=value per line. Compose reads it from the project directory.

Running without Compose

docker run -d -p 80:80 \
  -e TRACKER_URL="https://ipmagnetize.example.com/" \
  -v ipmagnetize-data:/var/www/data \
  ghcr.io/nkboedecker/ipmagnetize:1

The settings marked "override file" above become -e options. docker-compose.yml also sets a memory cap, a process limit, no-new-privileges and log rotation; add the matching docker run options if you want them. To run your own build, docker build -t ipmagnetize . in a checkout, and use ipmagnetize as the image name.

HTTPS with SWAG (optional)

docker-compose.swag.yml adds SWAG (nginx with automatic Let's Encrypt certificates) in front of ipMagnetize on ports 80 and 443, and sets TRUST_PROXY=true on ipMagnetize. Certificates are issued through Cloudflare's DNS-01 challenge, so the host doesn't need to be reachable during issuance.

Already running SWAG? Skip the overlay. Copy one of the templates from swag-templates/ into your existing SWAG's nginx/proxy-confs/, put ipMagnetize on the same Docker network as SWAG so the hostname ipmagnetize resolves, and set TRUST_PROXY=true.

Prerequisites

  • Your domain's DNS is hosted on Cloudflare.
  • An A record (plus AAAA if the host has IPv6) for ipmagnetize.<your domain> pointing at the host, set to DNS only. If Cloudflare proxies the record, every hit is logged with a Cloudflare address.
  • A Cloudflare API token with only Zone:DNS:Edit and Zone:Zone:Read, scoped to that one zone.

Setup

  1. Save the token where SWAG will look for it:

    mkdir -p swag-config/dns-conf
    echo "dns_cloudflare_api_token = <token>" > swag-config/dns-conf/cloudflare.ini
    chmod 600 swag-config/dns-conf/cloudflare.ini
  2. Create .env:

    SWAG_URL=example.com
    SWAG_EMAIL=you@example.com
    IPMAGNETIZE_BIND=127.0.0.1
    IPMAGNETIZE_PORT=8081
    COMPOSE_FILE=docker-compose.yml:docker-compose.override.yml:docker-compose.swag.yml
    • IPMAGNETIZE_BIND=127.0.0.1 keeps ipMagnetize's own port off the public interface, so only SWAG is reachable. The overlay can't do this itself: Compose adds up ports entries across files instead of replacing them.
    • COMPOSE_FILE makes plain docker compose commands use all three files. The commands below assume it's set. On Windows, separate the files with ; instead of :.
  3. Create docker-compose.override.yml with an https tracker URL:

    services:
      ipmagnetize:
        environment:
          TRACKER_URL: "https://ipmagnetize.example.com/"

    SWAG redirects all plain HTTP to HTTPS, and not every BitTorrent client follows redirects when announcing, so an http:// tracker URL can lose hits.

  4. Start everything, then watch SWAG obtain its certificate. Wait for Server ready:

    docker compose up -d
    docker compose logs -f swag
  5. Install the proxy config and reload nginx. swag-config/nginx/ only exists after SWAG's first start, which is why this comes last:

    cp swag-templates/ipmagnetize.subdomain.conf.sample swag-config/nginx/proxy-confs/ipmagnetize.subdomain.conf
    docker compose exec swag nginx -s reload

Open https://ipmagnetize.<your domain>/. The page should show your public IP as "the address you've accessed this page with". A 172.x.x.x address means TRUST_PROXY isn't in effect.

Subdomain or subfolder

Pick one. TRACKER_URL can only point at one of them.

  • Subdomain (ipmagnetize.subdomain.conf.sample) serves https://ipmagnetize.<your domain>/. The overlay is set up for this. The subdomain name is fixed to ipmagnetize. To change it, edit SUBDOMAINS in docker-compose.swag.yml and server_name in the proxy config.
  • Subfolder (ipmagnetize.subfolder.conf.sample) serves https://<your domain>/ipmagnetize/ from SWAG's default site. The certificate then has to cover your bare domain: set ONLY_SUBDOMAINS=false in docker-compose.swag.yml, and point the bare domain's DNS at this host. That moves anything currently served there. Set TRACKER_URL to https://<your domain>/ipmagnetize/.

With a subdomain, SWAG's default site still answers requests for the host's bare IP, or any other hostname, with a placeholder page. To close those connections without a reply, add return 444; inside the listen 443 ssl default_server block of swag-config/nginx/site-confs/default.conf and reload nginx. Skip this if you use the subfolder setup, or anything else on this SWAG uses subfolder configs: they're served from that default site.

Behind another reverse proxy

Set TRUST_PROXY=true only if ipMagnetize can be reached solely through your proxy. The proxy must set X-Real-IP to the client's address and overwrite any value the client sent. In nginx: proxy_set_header X-Real-IP $remote_addr;.

ipMagnetize uses the header only when all of these hold:

  • TRUST_PROXY is true.
  • The connection comes from a private or loopback address.
  • The header contains a valid IP address.

Otherwise it logs the connecting address. X-Forwarded-For is ignored, because clients can prepend fake entries to it. That differs from upstream's reverse-proxy advice, which doesn't apply to this fork.

Firewalls

Host firewalls such as UFW and firewalld don't filter ports that Docker publishes. Docker inserts its own iptables rules ahead of theirs, so ufw status can list only SSH while ipMagnetize's port 80, or SWAG's 80 and 443, are open to the internet. To restrict them, either:

  • Use a firewall in front of the host, such as a DigitalOcean Cloud Firewall or an AWS security group. Docker can't bypass it.
  • Add rules to Docker's DOCKER-USER iptables chain, which Docker checks before its own rules.

With the SWAG setup, IPMAGNETIZE_BIND=127.0.0.1 keeps ipMagnetize's own port off the network whatever the firewall rules are.

Updating

git pull
docker compose pull
docker compose up -d

docker compose up -d alone keeps running the image you already have. The published image is rebuilt every week on the newest php:8.4-apache, which is how PHP and Debian security fixes reach the container, so run these regularly even when there's nothing to pull from git. Your .env and override file aren't touched by git pull.

If you build from your checkout, run docker compose build --pull in place of docker compose pull: the app's code is built into the image, and --pull fetches the newest base image. Your build takes the published image's name, so a later docker compose pull replaces it with the published one.

SWAG is pinned to a tested release, so it only changes when this repository moves the pin (picked up by the commands above) or when you set SWAG_VERSION in .env to a tag from SWAG's releases and run docker compose up -d.

Image tags

ghcr.io/nkboedecker/ipmagnetize has these tags. Set IPMAGNETIZE_VERSION in .env to choose one.

Tag What it is
1.2.3 That release.
1.2 The newest 1.2.x release.
1 The newest 1.x release. docker-compose.yml uses this one unless you choose another.
latest The newest release.
edge The current master branch, which may not be released yet.

Every week the newest release is rebuilt on the newest php:8.4-apache, and its tags are moved to the rebuild, so the image behind a tag changes even without a new release. Only the newest release is rebuilt: once 1.3.0 is out, 1.2 stops getting security fixes.

Releasing

From master, tag the release and push the tag:

git tag -a v1.2.3 -m "ipMagnetize 1.2.3"
git push origin v1.2.3

The Docker image workflow builds the tag for both platforms, runs test/smoke-test.sh against it, and publishes it, then creates the release page on GitHub with the tag's message as its notes, so write the message for readers. Before a new major version, change the IPMAGNETIZE_VERSION default in docker-compose.yml to the new major version in the commit you tag. To test an image locally, build it and run sh test/smoke-test.sh <image>.

GitHub makes a new package private. After the first release, make it public: open the package from the repository's Packages list, then Package settings, and Change visibility under Danger Zone. GitHub also pauses scheduled workflows in a repository with no activity for 60 days, which stops the weekly rebuilds. Its Actions tab offers to turn the workflow back on.

Data and backups

Hits are stored in SQLite at /var/www/data/ipmagnetize.db3, in the ipmagnetize-data volume. To copy it out:

docker compose cp ipmagnetize:/var/www/data/ipmagnetize.db3 ./ipmagnetize-backup.db3

You can bind-mount a host directory at /var/www/data instead. The container fixes its ownership at startup. For users' privacy, upstream recommends wiping the database regularly.

Moving an install from the old names

Until October 2026 this fork used ipmagnet for the Compose service, its volume, the IPMAGNET_* settings and SWAG's subdomain. After pulling the rename, an existing install starts with an empty database unless you move it across:

  1. In .env, rename IPMAGNET_BIND, IPMAGNET_PORT and IPMAGNET_MEM_LIMIT to IPMAGNETIZE_BIND, IPMAGNETIZE_PORT and IPMAGNETIZE_MEM_LIMIT. In docker-compose.override.yml, rename the ipmagnet: service to ipmagnetize: and point TRACKER_URL at the new hostname.

  2. Remove the old containers (--remove-orphans catches the old ipmagnet one, which the compose files no longer name), create the new ones without starting them, and copy the database:

    docker compose down --remove-orphans
    docker compose up --no-start --build
    docker run --rm -v <project>_ipmagnet-data:/from:ro -v <project>_ipmagnetize-data:/to \
      alpine cp /from/ipmagnet.db3 /to/ipmagnetize.db3

    <project> is the Compose project name, by default the checkout's directory name. docker volume ls lists both volumes.

  3. With SWAG, swap the proxy config, then start everything:

    rm swag-config/nginx/proxy-confs/ipmagnet.subdomain.conf
    cp swag-templates/ipmagnetize.subdomain.conf.sample swag-config/nginx/proxy-confs/ipmagnetize.subdomain.conf
    docker compose up -d

    SWAG requests a certificate for ipmagnetize.<your domain> as it starts, so create that DNS record first. Magnet links handed out before the move still announce to the old hostname.

The old volume stays until you remove it with docker volume rm.

About

A containerized fork of ipMagnet with SWAG

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages