aMule is a multi-platform client for the ED2K file sharing network and based on the windows client eMule. aMule started in August 2003, as a fork of xMule, which is a fork of lMule.
The image is based on debian:trixie-slim and compiles aMule from source code.
Docker images are available in DockerHub and GHCR.
docker pull ngosang/amule
# or
docker pull ghcr.io/ngosang/amuleStable:
latest— Latest stable release.3.0.1-2— Specific stable version.
Development:
develop— Latest development build (compiled from aMule master branch).develop-20260512-abc1234— Specific development build: date + aMule upstream commit.
Debug:
debug— Latest debug build (compiled from aMule master branch).debug-20260512-abc1234— Specific debug build: date + aMule upstream commit.
Note
The debug images are compiled in Debug mode (CMAKE_BUILD_TYPE=Debug, full symbols, no optimizations) and ship with the debugging tools gdb, strace, lsof and heaptrack preinstalled, so you can debug crashes and memory issues of the aMule development builds. As a result they are larger and slower than the stable and develop images and are not intended for production use. The stable and develop images are regular optimized builds without these tools.
The architectures supported by this image are:
- linux/386
- linux/amd64
- linux/arm/v5
- linux/arm/v7
- linux/arm64/v8
- linux/ppc64le
- linux/riscv64
- linux/s390x
The Web UI is at <your-ip>:4711. It is served by amuleapi, the daemon added in aMule
3.1.0: it connects to aMule over External Connections and serves the Web UI at / and the
REST API under /api/v1/ on the same port.
The login form only asks for a password (there is no user name) and the password decides
the role: WEBUI_PWD logs in as admin (full control) and WEBUI_GUEST_PWD, if you set it,
as a read-only guest.
The REST API shares that port and those credentials. POST /api/v1/auth/login mints a JWT
and returns it as an HttpOnly amuleapi_token cookie, or in the response body when you
ask for it with ?type=bearer (for Authorization: Bearer clients). The admin password
unlocks every endpoint; the guest one is read-only and gets 403 on any mutation. See the
upstream REST reference
and event stream docs.
Important
Set GUI_PWD and WEBUI_PWD. If you leave them out, random passwords are generated on
the first start and only printed to the container logs (docker logs amule). GUI_PWD
is mandatory when you upgrade a configuration created by an older image, see
Upgrading to 3.1.0.
Note
amuleapi speaks plain HTTP, so don't expose port 4711 to the Internet directly. Put a
reverse proxy with TLS in front of it.
The Web UI and API settings live in their own amuleapi.conf file in the configuration
volume (bind address, port, CORS and StaticRoot); the [WebServer] section of
amule.conf is only used by the legacy Web UI. Restart the container after editing it.
To serve your own Web UI bundle instead of the one shipped in the image, mount it in the
container and set StaticRoot to its path. Leave StaticRoot= empty to serve the bundled
Web UI (/usr/share/amule/amuleapi-static).
Important
Stop the container before editing amule.conf. aMule keeps the configuration in memory
and rewrites the whole file on shutdown, so any change made while the container is
running is lost on the next restart.
Country flags are enabled by default. aMule downloads the free
DB-IP database (~5 MB) to geoip.mmdb in
the configuration volume and refreshes it monthly, no account needed. Disable it or pick
another source in Preferences -> IP2Country.
Media metadata extraction is enabled by default. aMule runs the bundled ffprobe on each
shared audio and video file to fill in the Length, Bitrate and Codec other clients see in
their search results. The first scan of a large library takes a while, one probe per file in
a background thread. Disable it in Preferences -> Files -> Media metadata extraction.
The previous Web UI (amuleweb) is deprecated and will be removed. It is still available
as a temporary fallback, see Legacy Web UI (amuleweb).
For better download speed you have to open these ports:
- 4662 TCP
- 4665 UDP
- 4672 UDP
Here are some example snippets to help you get started creating a container.
Note
When you start aMule all shared folders are scanned. The user interface will not be available until the process is finished. You can check the logs and CPU usage to know the status.
Compatible with docker-compose v2 schemas.
---
services:
amule:
image: ngosang/amule
container_name: amule
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- GUI_PWD=<fill_password>
- WEBUI_PWD=<fill_password>
- MOD_AUTO_RESTART_ENABLED=true
- MOD_AUTO_RESTART_CRON=0 6 * * *
- MOD_AUTO_SHARE_ENABLED=false
- MOD_AUTO_SHARE_DIRECTORIES=/downloads/incoming;/my_movies
ports:
- "4711:4711" # Web UI and REST API (amuleapi)
- "4712:4712" # External connections (amuleapi, amulegui, amulecmd)
- "4662:4662" # ED2K client-to-client TCP (required for High ID)
- "4665:4665/udp" # ED2K server UDP (global searches, TCP port +3)
- "4672:4672/udp" # Extended eMule protocol and Kademlia UDP
volumes:
- <fill_amule_configuration_path>:/home/amule/.aMule
- <fill_amule_downloads_path>:/downloads
restart: unless-stoppedNote
aMule stores completed downloads in /downloads/incoming and incomplete downloads in /downloads/temp inside the container. These paths can be changed with the INCOMING_DIR and TEMP_DIR environment variables. You can also mount /downloads/incoming and /downloads/temp as separate volumes, but be aware that completed files will be copied instead of moved, since they would reside on different filesystems.
docker run -d \
--name=amule \
-p 4711:4711 \
-p 4712:4712 \
-p 4662:4662 \
-p 4665:4665/udp \
-p 4672:4672/udp \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Europe/London \
-e GUI_PWD=<fill_password> `#recommended` \
-e WEBUI_PWD=<fill_password> `#recommended` \
-e MOD_AUTO_RESTART_ENABLED=true `#optional` \
-e 'MOD_AUTO_RESTART_CRON=0 6 * * *' `#optional` \
-e MOD_AUTO_SHARE_ENABLED=false `#optional` \
-e MOD_AUTO_SHARE_DIRECTORIES=/downloads/incoming;/my_movies `#optional` \
-v <fill_amule_configuration_path>:/home/amule/.aMule \
-v <fill_amule_downloads_path>:/downloads \
--restart unless-stopped \
ngosang/amuleContainer images are configured using parameters passed at runtime (such as those above). These parameters are separated by a colon and indicate <external>:<internal> respectively. For example, -p 8080:80 would expose port 80 from inside the container to be accessible from the host's IP on port 8080 outside the container.
| Parameter | Function |
|---|---|
-p 4711 |
Web UI and REST API port (amuleapi). |
-p 4712 |
External connections port (amuleapi, amulegui, amulecmd). |
-p 4662 |
ED2K client-to-client TCP (required for High ID). It must be open to the Internet. |
-p 4665/udp |
ED2K server UDP (global searches, TCP port +3). It must be open to the Internet. |
-p 4672/udp |
Extended eMule protocol and Kademlia UDP. It must be open to the Internet. |
-e PUID=1000 |
for UserID - see below for explanation. |
-e PGID=1000 |
for GroupID - see below for explanation. |
-e UMASK=0002 |
Set the umask for file creation. Optional, defaults to 0002 (files: 664, dirs: 775, group write access). |
-e TZ=Europe/London |
Specify a timezone to use EG Europe/London. |
-e GUI_PWD=<fill_password> |
Set the External Connections password, used by amuleapi, amulegui and amulecmd. It will overwrite the password in the config files. Required when upgrading a configuration created by an older image, see Upgrading to 3.1.0. |
-e WEBUI_PWD=<fill_password> |
Set the Web UI admin password. It will overwrite the password in the config files. |
-e WEBUI_GUEST_PWD=<fill_password> |
Set the Web UI guest password, a read-only account. Optional, leave it empty to disable the guest account. If you remove the variable entirely, whatever was set before is kept. |
-e WEBUI_ENABLED=true |
Start the Web UI and REST API service (amuleapi, or amuleweb when LEGACY_AMULEWEB_ENABLED=true). Optional, enabled by default. Set it to false to run a headless container with only the amuled daemon, reachable through External Connections on port 4712 (amulegui/amulecmd). |
-e LEGACY_AMULEWEB_ENABLED=false |
Start the deprecated legacy Web UI (amuleweb) instead of amuleapi, on the same port. Optional, disabled by default. See Legacy Web UI (amuleweb). |
-e TEMP_DIR=/downloads/temp |
Path inside the container for incomplete downloads. Optional, defaults to /downloads/temp. |
-e INCOMING_DIR=/downloads/incoming |
Path inside the container for completed downloads. Optional, defaults to /downloads/incoming. |
-e FIX_PERMISSIONS=true |
Change ownership of the temp and incoming directories at startup. Optional, enabled by default. Set it to false on network mounts (NFS, CIFS/SMB), where the ownership is dictated by the export or the mount options and chown is rejected. Make sure those paths are already accessible by PUID/PGID. |
-e MOD_AUTO_RESTART_ENABLED=true |
Enable aMule auto restart. Check modifications section. |
-e 'MOD_AUTO_RESTART_CRON=0 6 * * *' |
aMule auto restart cron mask. Check modifications section. |
-e MOD_AUTO_SHARE_ENABLED=false |
Enable aMule auto share. Check modifications section. |
-e MOD_AUTO_SHARE_DIRECTORIES=/downloads/incoming;/my_movies |
aMule auto share directories with subdirectories. Check modifications section. |
-v /home/amule/.aMule |
Path to save aMule configuration. |
-v /downloads |
Path to downloads. aMule uses /downloads/incoming for completed downloads and /downloads/temp for incomplete downloads. |
When using volumes (-v flags) permissions issues can arise between the host OS and the container, we avoid this issue by allowing you to specify the user PUID and group PGID.
Ensure any volume directories on the host are owned by the same user you specify and any permissions issues will vanish like magic.
In this instance PUID=1000 and PGID=1000, to find yours use id user as below:
$ id username
uid=1000(dockeruser) gid=1000(dockergroup) groups=1000(dockergroup)Version 3.1.0 replaces the legacy Web UI (amuleweb, deprecated upstream) with amuleapi
and its new Web UI, on the same port 4711. Read this before upgrading, and back up your
configuration volume first.
GUI_PWDis now mandatory if you never set it. amuleapi needs the External Connections password in plain text in its own config file, because it hashes the password itself: the MD5 hash stored inamule.confcannot be reused, it would be hashed twice, and upstream deliberately dropped the option to read it. So ifGUI_PWDis not set and the configuration volume already has anamule.conf, the container stops on start with an explanatory error. SetGUI_PWDto a password of your choice and remember to update youramulegui/amulecmdclients with it, since the container rewrites it inamule.conf.- The Web UI password is not migrated. If
WEBUI_PWDis not set, a new random admin password is generated on the first start with the new Web UI and printed to the container logs (docker logs amule). The oldamulewebpassword cannot be reused:amule.confstores it as a plain MD5 hash, while amuleapi keeps its own salted and stretched digest inamuleapi-passwords. - New files in the configuration volume:
amuleapi.conf(settings, including the plain text External Connections password),amuleapi-passwords(admin and guest passwords, salted and stretched) andamuleapi-jwt-secret(signs the login sessions; delete it to sign everyone out). amuleapi refuses to start if any of them is readable by group or others, so the container forces mode600on every start. - Optional read-only account: set
WEBUI_GUEST_PWDto enable it, leave it empty to disable it. Removing the variable keeps whatever was set before. - Web UI settings moved to
amuleapi.conf. The[WebServer]section ofamule.conf(Port,Template,UseGzip,PageRefreshTime, ...) is ignored by amuleapi, so any customization there has to be redone inamuleapi.conf, which only has the equivalents for the bind address, the port and the static files. Nothing is lost, the section stays inamule.confand is used again in legacy mode. - UPnP no longer forwards the Web UI port. Only relevant if you run with
network_mode: hostand hadUPnPWebServerEnabled=1: amuleapi has no UPnP support, so port4711has to be forwarded by hand from now on. See the UPnP section.
If the new Web UI gives you trouble you can roll back at any time with
LEGACY_AMULEWEB_ENABLED=true, see Legacy Web UI (amuleweb).
Note
The normal configuration uses the default bridge networking with the ports: mappings shown above, forwarding those ports on your router manually if needed. Only use UPnP if your router supports it and you specifically want automatic port forwarding, or if you are stuck with a Low ID and cannot forward ports by hand.
aMule is compiled with UPnP support, which lets aMule automatically open the required ports on a UPnP-capable router so you get a High ID without manually forwarding ports.
UPnP needs direct access to the host network to talk to the router, so it only works when the container runs with host networking. Set network_mode: host and do not add a ports: section: in host networking the container shares the host network stack directly, so the ports: mappings are ignored.
---
services:
amule:
image: ngosang/amule
container_name: amule
network_mode: host # required for UPnP (do not add a `ports:` section)
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- GUI_PWD=<fill_password>
- WEBUI_PWD=<fill_password>
volumes:
- <fill_amule_configuration_path>:/home/amule/.aMule
- <fill_amule_downloads_path>:/downloads
restart: unless-stoppedYou also have to enable UPnP in aMule itself, then restart the container. In amule.conf set UPnPEnabled=1, which forwards the eD2k TCP port (4662) and the UDP ports (4665, 4672). Optionally UPnPECEnabled=1 forwards the External Connections port.
Note
The Web UI port cannot be forwarded by UPnP: amuleapi has no UPnP support (UPnPWebServerEnabled belongs to the legacy Web UI). Forward 4711 by hand if you need it reachable from outside your network, behind a reverse proxy with TLS.
Note
UPnPTCPPort (default 50000) is not a forwarded port — it is the local port aMule's UPnP stack uses to communicate with the router. Leave it at the default unless it conflicts with another service. If you run a local firewall, allow local TCP 50000 and UDP 1900.
The Docker image includes some unofficial features. All of them are optional.
We have implemented a cron scheduler to restart aMule from time to time. To enable this mod set these environment variables:
MOD_AUTO_RESTART_ENABLED=trueMOD_AUTO_RESTART_CRON=0 6 * * *=> Cron mask is configurable. In the example it restarts everyday at 6:00h.
Note
Restarting aMule also restarts the Web UI: amuleapi loses its External Connections link to aMule and exits, and the supervisor starts it again a few seconds later. The Service amuleapi terminated with exit code: 1 line you see in the logs afterwards is expected, not an error.
By default, aMule only shares the "incoming" directory. The new Web UI can add and remove share roots itself (/api/v1/share_directories), so this mod is disabled by default. It is only useful with the legacy Web UI, which cannot select shares, or to declare shares from docker-compose without the UI.
When enabled, the mod writes the listed directories as recursive shared roots (shareddir-recursive.dat) every time the container starts, so aMule shares each directory together with all of its sub-directories. New sub-directories created later are shared automatically too (see AutoRescanSharedDirs below). aMule regenerates shareddir.dat (the union of all shared directories) on startup.
MOD_AUTO_SHARE_ENABLED=trueMOD_AUTO_SHARE_DIRECTORIES=/downloads/incoming;/my_movies=> List of directories separated by semicolon ';'. Subdirectories will be shared too.
Warning
Don't manage shares from both the Web UI and this mod: it rewrites shareddir-recursive.dat on every start, so shares added from the Web UI are lost on restart.
These options control how aMule scans the shared directories. Change them from the new Web UI, or by editing amule.conf in the config volume with the container stopped.
AutoRescanSharedDirs=1=> aMule watches the shared directories and detects changes (new files and sub-directories) automatically, without a manual "Reload shared files". New sub-directories under a recursive root are shared on the fly. Set to0to disable the watcher.FollowSymlinksInShares=1=> aMule follows symbolic links while scanning the shared directories. Set to0to skip symlinked files and directories entirely.ExcludeSharePatterns=>|-separated glob patterns of file names to skip while scanning. aMule 3.1.0 ships a default that hides OS metadata junk; it is not written toamule.confbut applies anyway, so add the key only to override it.ExcludeSharePatternsUseRegex=0=> Set to1to treatExcludeSharePatternsas a single regular expression instead of glob patterns.
Warning
amuleweb is deprecated upstream and will be removed from aMule, and from this image,
in a future release. It is kept only as a fallback while the new Web UI matures, so
please migrate to amuleapi instead of settling here.
Set LEGACY_AMULEWEB_ENABLED=true and the container starts amuleweb instead of
amuleapi, on the same port 4711. The amuleapi files in the configuration volume are
left untouched, so removing the variable switches back to the new Web UI. Note that
nothing amuleapi needs is created while this mode is on, so switching back on an existing
configuration still requires GUI_PWD, see Upgrading to 3.1.0.
In this mode the Web UI password is the WEBUI_PWD one stored in amule.conf, and there
is no REST API and no read-only guest account (WEBUI_GUEST_PWD is ignored).
The Docker image ships with the default aMule Web UI theme. The previously bundled AmuleWebUI-Reloaded theme has been removed because it is not compatible with aMule 3.0.0.
You can still use a custom theme by mounting it as an external volume inside the web
server templates directory (/usr/share/amule/webserver/<ThemeName>) and pointing the
Template option to it in the amule.conf file:
services:
amule:
# ... rest of the service definition ...
volumes:
- /path/to/my-theme:/usr/share/amule/webserver/MyThemeThen edit the amule.conf file (with the container stopped) and set Template=MyTheme.
Leave Template= empty to use the default theme. The theme directory must contain
login.php at its root, otherwise aMule silently falls back to the default theme.

