If you just want to give I2P+ a quick try or are using it on a home network, follow these steps:
docker pull ghcr.io/i2pplus/i2pplus:latest
docker run -d -p 7657:7657 -p 7667:7667 -p 4444:4444 ghcr.io/i2pplus/i2pplus:latest
Then open http://127.0.0.1:7657 in your browser. The web console binds to 0.0.0.0 (all interfaces) inside the container, so you can also access it at your Docker host’s IP address. The I2NP external port is assigned randomly - check your router console (Network section) or router.config for the assigned port. For full network participation, you’ll need to map this port externally.
git clone https://github.com/I2PPlus/i2pplus.gitdocker/docker-compose.yml to docker-compose.yml in the root directory of your local I2P+ git workspacedocker-compose up --buildhttp://127.0.0.1:7657 and then hit the Wizard link to configure your routerdocker-compose downdocker system prune -a -fBy default the image limits the memory available to the Java heap to 1024MB. You can override this at runtime with the -e JVM_XMX=2048m flag, or by modifying the JVM_XMX environment variable in the docker/rootfs/startapp.sh file before building.
The container runs as a non-root user i2p for security. If you need to exec into the container for debugging, note that you are running as user i2p.
By default the i2p user is created with UID 1000 and GID 1000. On most Linux systems the first user is 1000, so this works out of the box. If your host user has a different UID/GID (check with id), volume-mounted files will be owned by the container’s UID/GID instead of yours, causing permission errors.
Fix at build time with docker compose (recommended):
Create a .env file in the project root (next to docker-compose.yml):
UID=1001
GID=1001
Then rebuild:
docker compose up --build -d
Or pass build args directly:
docker compose build --build-arg UID=1001 --build-arg GID=1001
docker compose up -d
Or with plain docker build:
docker build --build-arg UID=1001 --build-arg GID=1001 -t i2pplus -f docker/Dockerfile .
Note: Changing UID/GID requires a full rebuild (
docker compose build --no-cache). A previously built image with UID 1000 will still use 1000 regardless of later.envchanges.
The image includes a HEALTHCHECK that monitors the router every 5 minutes. You can verify container health with:
docker inspect --format='' i2pplus
For enhanced security, you can run with a read-only root filesystem:
docker run --read-only --tmpfs /i2p/.i2p:rw --tmpfs /i2psnark:rw ...
There are several ports which are exposed by the image. You can choose which ones to publish depending on your specific needs.
| Port | Interface | Description | TCP/UDP |
|---|---|---|---|
| 4444 | 127.0.0.1 | HTTP Proxy | TCP |
| 6668 | 127.0.0.1 | IRC Proxy | TCP |
| 7654 | 127.0.0.1 | I2CP Protocol | TCP |
| 7656 | 127.0.0.1 | SAM Bridge TCP | TCP |
| 7657 | 0.0.0.0 | Web Console (non-SSL) | TCP |
| 7667 | 0.0.0.0 | Web Console (SSL) | TCP |
| 7658 | 127.0.0.1 | I2P Webserver | TCP |
| 7659 | 127.0.0.1 | SMTP Proxy | TCP |
| 7660 | 127.0.0.1 | POP Proxy | TCP |
| 7652 | LAN interface | UPnP | TCP |
| 7653 | LAN interface | UPnP | UDP |
| RANDOM | 0.0.0.0 | I2NP Protocol | TCP+UDP |
At the minimum, you’ll want the Router Console (7657) and the HTTP Proxy (4444) available on localhost or your LAN network. Most services bind to 127.0.0.1 and will only be available inside the container. The web console binds to 0.0.0.0 so it’s accessible from your Docker host by default — both the non-SSL (7657) and SSL (7667) ports are bound. Services should not be exposed to the public internet. They can be disabled in the I2P+ web console if not required.
Security note: Binding the web console to
0.0.0.0makes it accessible on all network interfaces. If your Docker host is exposed to the internet, restrict access by settingIP_ADDRto your LAN IP (e.g.,192.168.1.10) or use Docker’s-p 127.0.0.1:7657:7657to limit exposure to localhost only. Without these precautions, the console may be reachable from the public internet if firewall rules permit.
To receive inbound connections from peers, you must expose the external I2NP port (TCP+UDP). Without it, the router will show as firewalled and rely on hole punching, which is less reliable. See External Network Port below for how to set a fixed port for port forwarding.
By default, I2P+ uses a random port for the I2NP Protocol (TCP+UDP). This is recommended for security - avoid using a fixed port as it’s fingerprintable. The port is assigned at first start and remains consistent for that container (stored in router.config).
Set at build time:
docker build --build-arg EXTERNAL_PORT=12345 -t i2pplus .
Or set at runtime:
docker run -e EXTERNAL_PORT=12345 i2pplus:latest
Your allocated port will be listed in your Router Web Console at http://127.0.0.1:7657/info. Note: This is the only port that you need to expose to the public internet, access to other ports should only be permitted from localhost or your LAN.
# From project root
docker build -t i2pplus:latest -f docker/Dockerfile .
# Save to file for transfer
docker save i2pplus:latest -o /tmp/i2pplus.tar
# Load from file
docker load -i /tmp/i2pplus.tar
# Basic run (with port mapping for console access)
docker run -d -p 7657:7657 -p 7667:7667 -p 4444:4444 --name i2pplus i2pplus:latest
# With persistent config (survives container restart)
# Note: The external I2NP port is random - see your router console or router.config for the assigned port
docker run -d -v /path/to/i2p-data:/i2p/.i2p \
-v /path/to/snark:/i2psnark \
-p 7657:7657 -p 7667:7667 -p 4444:4444 \
--name i2pplus i2pplus:latest
# Override JVM heap size
docker run -d -e JVM_XMX=2048m i2pplus:latest
# Override JAVA options
docker run -d -e JAVA17OPTS="-XX:+UseG1GC" i2pplus:latest
# View logs
docker logs i2pplus
docker logs -f i2pplus # follow
# Interactive shell (for debugging)
docker exec -it i2pplus /bin/bash
# Stop/Start
docker stop i2pplus
docker start i2pplus
# Remove container
docker rm -f i2pplus
# Start
docker-compose up --build -d
# View logs
docker-compose logs -f
# Stop
docker-compose down
# Check container status
docker ps -a
# Check resource usage
docker stats i2pplus
# Check configuration
docker exec i2pplus cat /i2p/router.config
# Shell script syntax check
bash -n docker/rootfs/startapp.sh && echo "OK"
# Stop container (if running) and remove
docker stop i2pplus 2>/dev || true
docker rm -f i2pplus
# Remove volumes (config, plugins, reseed data)
docker volume rm i2pplus_i2p-home # or your volume name
docker volume rm i2pplus_i2psnark # or your volume name
# Or if using bind mounts, delete the host directories
rm -rf /path/to/i2p-data
rm -rf /path/to/snark
# Rebuild and start fresh
docker build -t i2pplus:latest -f docker/Dockerfile .
docker run -d -v /path/to/i2p-data:/i2p/.i2p -v /path/to/snark:/i2psnark i2pplus:latest
# Enter container
docker exec -it i2pplus /bin/bash
# Clear router cache
rm -rf /i2p/.i2p/routerCache
# Clear profile (WARNING: this deletes ALL data including config - only use if you want a fresh start)
rm -rf /i2p/.i2p/
# Clear I2PSnark torrents
rm -rf /i2psnark/*
# Exit container and restart
exit
docker restart i2pplus
# Use the cleanup script (recommended)
./docker/cleanup.sh --all
# Or manually:
# Remove stopped containers
docker container prune -f
# Remove unused images
docker image prune -a -f
# Remove unused volumes
docker volume prune -f
# Full cleanup
docker system prune -a -f
The cleanup.sh script provides an interactive way to clean up specific resources:
./docker/cleanup.sh --help