Diagnostics avancés¶
Ces sections vont plus loin que le premier déploiement : téléchargement, High-ID, stockage,
récupération. La plupart restent accessibles (lecture de logs, redémarrage de processus), mais
High-ID/port-sync et Stockage & droits exigent une familiarité Linux/Docker et le signalent à
leur ouverture. Si une étape dépasse votre confort, l'option de repli sûre est presque toujours de
repartir d'un dossier data/ vide (voir « Récupération après panne ») : vous perdez le catalogue
accumulé, mais vous redémarrez d'un état connu.
Où lancer ces commandes
Depuis votre dossier de travail, celui qui contient compose.yml. Les mêmes commandes servent
les deux variantes : c'est la ligne include: de compose.yml qui choisit. Le crawler est le
service p2pwatch, le client eMule le service ed2k, et son tunnel ed2k-gluetun sous la
variante VPN.
Démarrage & réseau¶
amuled ne se connecte à aucun serveur ni réseau (tunnel)¶
- Cause la plus fréquente. Au tout premier run, amuled doit amorcer sa liste de serveurs eD2k
(
server.met) et de nœuds Kad (nodes.dat) en faisant du DNS et du HTTPS sortant (443) à travers le VPN. Si gluetun n'est pas encore monté, ou si la sortie Internet est bloquée à ce moment, rien ne s'amorce et amuled reste sans serveurs ni nœuds. - Solution. Vérifiez d'abord l'état du tunnel, avant amuled :
Ce que vous devez voir (tunnel sain) :
[gluetun] [main] Listening on 0.0.0.0:8000
[gluetun] [main] You are running on the public IP address W.X.Y.Z ← IP VPN (pas la vôtre !)
[gluetun] [vpn] connected
[gluetun] [vpn] cannot connect to ... ← VPN provider/clé refusée
[gluetun] [main] retrying in N seconds
ed2k partage le réseau d'ed2k-gluetun (network_mode:
service:ed2k-gluetun) : tant que le tunnel est down, amuled n'a aucune sortie. Le crawler, lui,
reste hors du tunnel. Si le tunnel ne monte pas, corrigez le VPN dans .env (clé WireGuard,
fournisseur, SERVER_COUNTRIES), puis recréez les deux services d'aMule : ed2k vit dans le
namespace réseau d'ed2k-gluetun, il doit être recréé avec lui.
Si le tunnel est seulement monté en retard, sans rien modifier, redémarrer le processus amuled
suffit :
- Version d'aMule. Elle n'est pas un paramètre de déploiement : aMule est compilé sur Debian
dans notre propre image, p2pwatch-amule, depuis un commit git épinglé dans son Dockerfile. Sa
version suit donc celle de l'image. Pour savoir laquelle tourne :
docker compose exec ed2k amuled --version. La commande sort en code 255 même quand tout va bien.
s6 a redémarré un processus et le conteneur est resté debout¶
- Symptôme. amuled réapparaît dans
docker compose logs ed2k(il se ré-annonce, rechargeserver.met) alors quedocker compose psn'a jamais quittéUppoured2k. - Cause. C'est normal. Dans le conteneur
ed2k, s6 supervise deux services,amuledet le port-sync, et relance sur place celui qui meurt. amuleapi n'en fait pas partie :amuledle démarre et l'arrête avec lui, donc un redémarrage d'amuleden entraîne un d'amuleapi. Le crawler n'est pas sous s6 : c'est Docker qui relance le conteneurp2pwatchaprès chacune de ses sorties, y compris celle que demande le bouton de redémarrage du tableau de bord.ed2kgarde alors ses sessions eD2k et Kad, ce qui est tout l'intérêt. - Comment le confirmer.
s6-svstataffiche l'uptime du service en secondes : un petit nombre signifie qu'il vient d'être redémarré. Pour le crawler, la colonneSTATUSdedocker compose psdonne la même information. - Conséquence à garder en tête.
healthyne concerne queed2k, et ne dit qu'une chose : amuled tourne. Le crawler n'a pas de healthcheck. Un crawler qui tombe en boucle se voit enRestartingdansdocker compose ps; un crawler vivant écrit des lignesverdict(s) changeddansdocker compose logs p2pwatch, une par recherche terminée.
Le crawler refuse de démarrer : « environment variable '…' referenced but not set »¶
- Symptôme.
docker compose psmontrep2pwatchenRestarting, etdocker compose logs p2pwatchafficheInvalid config, refusing to start: … : environment variable 'DISCORD_WEBHOOK_ID' referenced but not set, alors que la variable est bien renseignée dans.env. - Cause. Compose ne lit
.envque pour substituer les${...}dans les fichiers compose. Le crawler, lui, interpole les${VAR}decrawler.ymldepuis son propre environnement de conteneur. Une variable référencée danscrawler.ymldoit donc être injectée explicitement dans le servicep2pwatch(blocenvironment:decompose.yml), sinon le process ne la voit pas. SeulAMULE_API_PASSWORDy est câblé par défaut. - Solution. Si vous ajoutez un nouveau
${VAR}danscrawler.yml, typiquement en activant une URL de notificationnotifications[].url: "discord://${DISCORD_WEBHOOK_ID}/…", ajoutez la même variable au blocenvironment:du servicep2pwatch:compose.ymlLe mapping est explicite (et nonp2pwatch: environment: AMULE_API_PASSWORD: ${AMULE_API_PASSWORD:?} DISCORD_WEBHOOK_ID: ${DISCORD_WEBHOOK_ID:?} # ← nouvelle ligne par secret ajouté DISCORD_WEBHOOK_TOKEN: ${DISCORD_WEBHOOK_TOKEN:?}env_file: .env) pour le moindre privilège : le crawler n'a à voir ni la clé WireGuard, ni le mot de passe EC, ni les autres secrets du déploiement.
Le statut « Low-ID » apparaît dans les logs¶
- Ce n'est pas une panne. Low-ID est l'état normal par défaut : recherche, catalogage et
téléchargement fonctionnent, seule la joignabilité est sous-optimale (moins de sources directes).
Le tableau de bord le montre aussi :
Connectabley vautnosur le canaled2k. - Pour passer en High-ID (optionnel), voir Devenir High-ID.
Téléchargements¶
Un fichier terminé n'apparaît jamais dans ed2k/downloads/incoming¶
- Ce qui se passe normalement. amuled écrit un fichier terminé directement dans son
IncomingDir, que le conteneured2katteint par le bind mount./downloads:/downloadsde son dossiered2k/. Le crawler détecte la complétion depuis la liste des fichiers partagés d'amuled (le hash est partagé et a quitté la file de téléchargement), passe le téléchargement encompletedet notifie. Il ne déplace, n'ouvre ni n'inspecte jamais le fichier : il monteed2k/downloadsen lecture seule, et n'y fait qu'unstatvfs, pour le plancher d'espace libre. - Regardez d'abord l'état que voit le crawler. Si la webui montre encore le téléchargement en
downloading, c'est qu'il n'est tout simplement pas fini : rien n'est cassé. - Si le crawler dit
completedmais que le dossier est vide, amuled a posé le fichier ailleurs. Deux causes, dans l'ordre : - Une catégorie amuled redirige la destination. Dans
amule.conf(ou via l'interface d'aMule sur le port 4711), aucune catégorie ne doit porter unPath=non vide qui envoie le fichier terminé hors d'IncomingDir. IncomingDirne pointe pas sur le chemin monté en bind. Le one-shot de démarrage d'ed2kécritIncomingDir=/downloads/incomingetTempDir=/downloads/tempdansamule.conf, mais seulement quand ce fichier est absent. Un nœud migré depuis une organisation plus ancienne porte son propreamule.conf, donc une vieille valeur survit à tous les redémarrages.amule.confest un simple fichier de votre dossier de travail, lisez-le depuis l'hôte : Corrigez les deux lignes, puis redémarrez amuled seul :- Gardez amuled dédié au crawler. Ses fichiers partagés sont lus à chaque cycle de
téléchargement : ne pointez donc pas cet amuled sur une grande bibliothèque partagée
préexistante, la détection de complétion en deviendrait plus lente et plus bruyante. Contexte et
sources :
reference/2026-06-17-amuled-completion-behavior.md(ses contraintes 1 et 2, sur un volume de quarantaine partagé, ne s'appliquent plus : l'étape de quarantaine a été retirée le 2026-09-13).
Un téléchargement est bloqué, puis passe en failed¶
- Ce que fait le crawler. Chaque cycle de téléchargement horodate
latest_seen_atpour chaque fichier suivi qu'amuled rapporte, que ce soit dans sa file de téléchargement ou dans ses fichiers partagés. Un téléchargementqueuedoudownloadingqu'amuled ne rapporte pas lui est renvoyé à chaque cycle, jusqu'à ce qu'il le rapporte de nouveau. S'il ne l'a plus rapporté depuisdownload.lost_after_seconds(24 h par défaut), il est marquéfailed, avec une ligne de journal :file=... unseen by the client for 86400.0s: marked failed. - Comment l'absence se compte. Sur l'horloge monotone du nœud, qu'un réglage de l'heure système
ne déplace pas. Au démarrage, le décompte reprend depuis
latest_seen_at: un téléchargement absent depuis plus dedownload.lost_after_seconds, arrêt du nœud compris, passe enfaileddès le premier cycle. Une heure système reculée pendant l'arrêt ne fait jamais attendre plus dedownload.lost_after_seconds; une heure avancée pendant l'arrêt compte, elle, comme du temps écoulé. - Pourquoi c'est sans danger. Une entrée reste dans la file d'amuled même avec zéro source : elle devient dormante, mais elle ne disparaît pas. Un téléchargement de lost media qui stagne à 0 % pendant des mois n'est donc jamais menacé. Une absence côté amuled signifie que l'entrée a réellement été retirée, et le crawler la remet alors en file.
- Ne sortez pas un fichier d'
IncomingDiravant que le téléchargement soitcompleted. Absent à la fois de la file et des fichiers partagés, il serait renvoyé à amuled et téléchargé de nouveau en entier. failedn'est pas définitif. amuled reste l'autorité : si le hash réapparaît dans sa file sans erreur, le crawler remet le téléchargement endownloading; s'il apparaît dans les fichiers partagés, le téléchargement se termine et la notification part. Un fichier qu'amuled signale en erreur restefailed, avec la raisonerrorsur/node. Regardezed2k/downloads/incomingavant de conclure que le fichier est perdu.- Pour en réessayer un à la main, supprimez sa ligne :
is_tracked()ignore l'état, donc une lignefailedcontinue de bloquer la remise en file automatique, à dessein. Il n'existe pas de contrôle webui pour cela et la console SQL est en lecture seule : c'est donc une écriture manuelle surlocal.db. Remplacez<hash>par le hash eD2k de la ligne à supprimer :
docker compose stop p2pwatch # (1)!
docker compose run --rm p2pwatch python -c \
"import sqlite3; db = sqlite3.connect('/data/local.db', autocommit=True); \
db.execute('DELETE FROM downloads WHERE native_id = ?', ('<hash>',))" # (2)!
docker compose start p2pwatch # (3)!
- On arrête le crawler seul : il est l'écrivain unique de
local.dbpar doctrine.ed2kcontinue de tourner, donc les sessions eD2k et Kad survivent. - Un conteneur jetable de la même image, avec les mêmes montages et sous le même
PUID:PGIDque le crawler : les fichiers WAL créés par SQLite restent donc les vôtres. Il ne publie aucun port et ne démarre rien d'autre. - Une fois le crawler relancé, le cycle suivant remet le fichier en file depuis la décision du
catalogue, à condition qu'il corresponde toujours à une cible qui n'est pas
complete. - Pour abandonner un téléchargement, l'annuler dans l'interface d'aMule ne suffit pas : amuled
accepte de nouveau le lien d'un fichier annulé, et le cycle suivant le lui renvoie. Marquez aussi
sa ligne
failed, qu'aucun cycle ne relance : arrêtez le crawler comme ci-dessus, annulez le téléchargement dans aMule, puis lancez la même commande avec cette écriture à la place duDELETE, qui ferait l'inverse, et relancez le crawler :db.execute('UPDATE downloads SET state = ? WHERE native_id = ?', ('failed', '<hash>')). - Si rien du tout ne se télécharge, vérifiez le plancher disque avant de soupçonner le TTL. Une
ligne de journal
candidate file=... → skip_disk_cap (skipped/deferred)signifie que l'espace libre, moins ce qu'amuled doit encore récupérer, passerait sousdownload.min_free_bytes. Unoutput directory unmeasurablesignifie au contraire que le montage./ed2k/downloads:/downloads:romanque au servicep2pwatchdecompose.yml.
High-ID / port-sync¶
⚠️ Prérequis pour ce diagnostic : lecture de logs gluetun et notions de port forwarding VPN. Si vous n'êtes pas à l'aise avec ces concepts, le port-sync n'est probablement pas la bonne voie pour vous : envisagez la Route B (port-forward manuel sur votre box) ou restez en Low-ID, qui marche très bien. Voir Devenir High-ID.
Le port-sync reste inopérant (toujours Low-ID alors qu'il est activé)¶
Le port-sync tourne dans le conteneur ed2k et écrit chacune de ses actions et chacun de ses
échecs dans docker compose logs ed2k. Plusieurs causes, à vérifier dans cet ordre :
- Il n'est pas démarré. Il ne tourne que sous la variante VPN (
ed2k/vpn.compose.yml), et seulement quandVPN_PORT_FORWARDINGl'allume : c'est son unique interrupteur,crawler.ymln'y joue aucun rôle. Vérifiez-le :true true: il tourne.false false: il est arrêté à dessein, soit parce que la ligneinclude:désigne encoreed2k/direct.compose.yml, soit parce queVPN_PORT_FORWARDINGvautoffou manque dans.env. Corrigez, puisdocker compose up -d. - Fournisseur sans port forwarding. Le High-ID exige un fournisseur à port forwarding
(Proton/PIA/PrivateVPN/PerfectPrivacy). Le journal le montre à chaque tour :
Une ligne
gluetun's control server is unavailable (...)signifie au contraire que le port-sync ne joint pas gluetun du tout :GLUETUN_CONTROL_URLest faux, oued2k-gluetunn'est pas monté. ed2kredémarre en boucle. Une valeur que le port-sync ne comprend pas fait sortir le conteneur dès son démarrage, avant amuled, sur une seule ligne qui nomme la variable, par exemplePORT_SYNC must be one of enabled, yes, on, true, disabled, no, off, false, got 'maybe'. IciPORT_SYNCreçoitVPN_PORT_FORWARDINGtel quel : corrigez cette dernière dans.env.- Le redémarrage d'amuled échoue. Pour appliquer un nouveau port, le port-sync arrête amuled,
écrit le port dans
amule.conf, puis le relance toujours. Une lignes6-svc -wD -T 60000 -d exited ...dit que l'arrêt n'a pas abouti : amuled a mis plus de 60 secondes à s'arrêter, ou le port-sync n'a pas reçu le droit de le piloter. Rien n'a été écrit, amuled est relancé, et le port-sync réessaie après 5 minutes. Si la ligne revient à chaque tentative, redémarrez le conteneur (docker compose restart ed2k) : le script de démarrage du port-sync lui redonne ses droits sur amuled. amule.confillisible.amule.conf's port is unreadable, amuled left as is (...): la lignePort=de la section[eMule]deed2k/amule/amule.confn'est pas un nombre, ou le fichier est abîmé. Le port-sync ne touche à rien tant que vous ne l'avez pas corrigé.amule.confabsent.amule.conf does not exist, amuled left as is(ouamule.conf no longer exists once amuled stopped, nothing written, si le fichier a disparu pendant le redémarrage) :ed2k/amule/amule.confa été supprimé. Redémarrez le conteneur (docker compose restart ed2k) : son étape de démarrage le recrée.
Un changement réussi s'annonce par gluetun forwards <N>, amuled listens on <M>: restarting
amuled. Si la ligne revient toutes les minutes avec un port différent, voyez
« Le port forwardé change toutes les minutes ».
Stockage & droits¶
⚠️ Prérequis pour cette section : Linux + notions d'UID/GID et de droits de fichiers. Si vous bloquez sur un de ces diagnostics et n'êtes pas à l'aise, l'option de repli sûre est de repartir d'un
data/vide, au prix du catalogue accumulé : voir « Je veux repartir de zéro ».
Un conteneur ne peut pas écrire dans ses dossiers (PUID / PGID)¶
- Ce que font les deux images. Il n'y a aucun volume nommé : tout est un bind mount relatif
dans votre dossier de travail (
data/,ed2k/amule/,ed2k/downloads/, plus les trois.ymlmontés en lecture seule). Le crawler tourne directement sous votrePUID:PGID(user:danscompose.yml) et ne change la propriété de rien :data/doit déjà vous appartenir. Au démarrage d'ed2k, le one-shotpython -m p2pwatch_amule boottourne en root, crée l'utilisateuramuleavecPUID:PGID, puis lui donne les points de montage (/home/amule/.aMule,/downloads/incoming,/downloads/temp). - Ce qu'il ne fait pas : ce
chownn'est PAS récursif sured2k/downloads/ni sured2k/amule/, et c'est délibéré. Ces dossiers peuvent contenir des centaines de gigaoctets de part files, et leur contenu appartient à l'opérateur. - Symptôme. Le crawler redémarre en boucle sur
unable to open database file:data/manquait au premierdocker compose up, et Docker l'a créé au nom de root. Ou bien, après un changement dePUID/PGIDou la migration d'un ancien nœud, amuled journalise une erreur d'écriture ou de permission sur son dossier temp ou incoming, ne reprend pas ses téléchargements en cours, ou ne relit pas sonamule.conf. Côté hôte, symptôme jumeau :sqlite3 data/catalog.dbou un simplels ed2k/downloads/demandesudo. - Cause. Les contenus existants appartiennent encore à un autre uid/gid, ou à root.
- Solution. Alignez
PUID/PGIDsur VOTRE utilisateur (c'est leur raison d'être : garder ces dossiers lisibles sanssudo), puis reprenez la propriété des contenus, une seule fois : - Posture de confinement, pour mémoire. Le PID 1 d'
ed2ktourne en root : il crée l'utilisateur et prend possession des points de montage, ce qui exclutuser:,read_only:etcap_drop: ALLsur ce service. Le crawler tourne sous votre identifiant, sansread_only:nicap_drop: ALLnon plus, par choix. Les deux gardentno-new-privileges:true,pids_limit: 512etmem_limit: 2g. Le détail et le risque accepté sont dans Limites connues.
Récupération après panne¶
Quelques scénarios « j'ai cassé quelque chose, comment je remonte ? » :
J'ai perdu / je ne me souviens plus de AMULE_EC_PASSWORD¶
- Symptôme. amuleapi n'arrive plus à joindre amuled : la page du port 4711 se charge mais ne
montre aucun transfert, et le crawler journalise des
503 ec_unavailable. - Le piège. Ce mot de passe ne sert qu'au lien interne entre amuleapi et amuled, dans le
conteneur
ed2k. Le one-shot de démarrage l'aligne dansed2k/amule/amule.confà chaque boot, donc le changer dans.envsuffit : un redémarrage du nœud propage la nouvelle valeur des deux côtés. - Solution. Choisissez un nouveau mot de passe, mettez-le dans
.env, puis : Pas de perte de catalogue : le mot de passe ne protège que le canal interne, pas les données. Si vous avez oubliéAMULE_API_PASSWORDà la place, la marche à suivre est la même : le one-shot le réécrit dansed2k/amule/amuleapi-passwordsà chaque boot, et la recréation le donne aussi au crawler. Pensez seulement à reporter la nouvelle valeur partout où vous vous connectiez avec l'ancienne. - Variante brutale. Supprimer
ed2k/amule/amule.confle fait régénérer au prochain démarrage, avec le mot de passe de.env. Vous perdez en revanche tous les autres réglages aMule accumulés dans ce fichier ; les serveurs eD2k et les nœuds Kad, eux, vivent dansserver.met/nodes.datet survivent.
J'ai mal édité .env et le compose refuse de démarrer¶
- Symptôme.
docker compose upretourne une erreur de parsing ou un serviceExited (1)immédiatement. - Solution. Recommencez à partir du modèle :
cp .env.example .env.new, recopiez vos secrets un par un en vérifiant la syntaxe (pas d'espaces autour du=, pas de guillemets autour des valeurs sauf nécessaire), puismv .env.new .env. Évite d'avoir à débugger un fichier corrompu. Si l'erreur nomme une variable (required variable "..." is not set), voir « Une variable obligatoire manque ».
Où trouver un fichier téléchargé ?¶
- Réponse. Dans
ed2k/downloads/incoming, à l'intérieur de votre dossier de travail. C'est un simple dossier de votre disque, donc arrêter ou supprimer les conteneurs n'y touche pas. Les fichiers encore en cours de téléchargement sont dansed2k/downloads/temp. - Rien n'a inspecté ce fichier. p2pwatch n'ouvre jamais un fichier téléchargé : pas de contrôle de type, pas de sonde média, pas d'analyse antivirus. Vérifiez-le vous-même avant de l'ouvrir.
Je veux repartir de zéro (catalogue effacé)¶
- Solution destructive (irréversible). Il n'y a pas de volume Docker à supprimer : les données
sont des dossiers de votre dossier de travail. Arrêtez la pile, effacez ce que vous voulez perdre,
puis recréez
data/vous-même : laissé à Docker, il serait créé au nom de root, et le crawler ne pourrait pas y écrire. Videz aussied2k/amule/pour repartir d'un aMule vierge (mot de passe, serveurs, nœuds Kad), eted2k/downloads/pour jeter les fichiers téléchargés ; ces deux-là sont indépendants du catalogue.docker compose down -vn'efface rien de tout cela. Sauvegardez d'abord ce que vous tenez à garder.
Outils de diagnostic¶
Piloter un service du conteneur ed2k¶
Dans ed2k, s6 supervise deux services, amuled et port-sync : ils se pilotent par service s6,
et non par service compose. Depuis votre dossier de travail (<svc> vaut amuled ou port-sync ;
amuleapi suit amuled, qui le démarre) :
docker compose exec ed2k s6-svstat /etc/services.d/<svc> # actif/arrêté + durée en secondes
docker compose exec ed2k s6-svc -r /etc/services.d/<svc> # le redémarrer
docker compose exec ed2k s6-svc -d /etc/services.d/<svc> # l'arrêter
docker compose exec ed2k s6-svc -u /etc/services.d/<svc> # le relancer
Le crawler, lui, se pilote en conteneur (docker compose stop|start|restart p2pwatch), sans toucher
amuled. Arrêter amuled rend au contraire le crawler aveugle, et emporte amuleapi : il notifie
au-delà de 2 minutes. Voir Faire tourner un nœud.
Valider la configuration sans rien démarrer¶
docker compose run --rm p2pwatch python -m p2pwatch validate-config \
--config /app/config/crawler.yml --targets /app/config/targets.yml \
--matcher /app/config/matcher.yml
Charge + valide les 3 configs et sort en erreur (code ≠ 0) si l'une est invalide, sans rien démarrer : un conteneur jetable lit les fichiers montés, puis disparaît. Il marche aussi quand le crawler redémarre en boucle. À lancer avant un déploiement (entre les étapes 3 et 4 d'Installer un nœud) ou après une modification de config.