onionize
Put any Docker service on Tor with one env var.
Point it at a container or a port on your host. It writes the config, starts Tor, and prints the .onion address. Nothing gets published on your host.
Interactive setup: pick your network and container from a list
$ bash <(curl -fsSL onionize.dev/run.sh)
Or add it to your compose file
services:
app:
image: nginx:alpine
onion:
image: ghcr.io/abiabdillahx/onionize
depends_on: [app]
environment:
TARGET: app:80
volumes:
- onion-keys:/var/lib/tor
volumes:
onion-keys:
- $ bash <(curl -fsSL onionize.dev/run.sh)
- Where does the service run?
- 1) Docker container
- 2) Directly on this host
- > 1
- Which Docker network? > myapp_default
- Which container? > myapp-web-1
- Container port [5000]: ⏎
- Onion port [80]: ⏎
- Start it now? [Y/n] ⏎
- waiting for the onion address ........
- onion is up:
- http://xxxxxxxxxxxxxxxx.onion -> myapp-web-1:5000
- open it in Tor Browser.
How it behaves
- One env var
TARGET=app:80is the whole configuration. The torrc, hostname lookup and onion port are generated for you.- No ports published
- The app is reachable only through its
.onionaddress, so there are no host port conflicts to sort out. - Same address every time
- The key lives in a volume. Recreate or update the container and the address stays the same.
- Many services, one address
TARGET=app:3000,443:app:3443maps each onion port to its own service.
Before you ship
- An onion service isn’t an anonymous app. Check for absolute links to your clearnet domain, external fonts and CDNs, and headers that name your stack.
- Back up the key volume. If it’s lost, the address is gone for good.
- Anyone with the address can open it. Client authorization isn’t supported yet.
- The target is resolved at startup. If the app container gets a new IP, restart the onion container.