Modèle Hugging Face dans une image Docker : quatre réglages pour qu'il soit vraiment là

Le modèle est téléchargé au build, et au démarrage le conteneur le cherche ailleurs ou le retélécharge. Quatre réglages que j'applique maintenant à chaque image, appris à la dure sur mon ml-runner.

Mon ml-runner embarque BGE-M3 dans son image Docker, pour démarrer sans téléchargement. Sur le papier, c'est simple. En pratique, j'ai eu droit à un modèle téléchargé au build et introuvable au démarrage. Depuis, j'applique quatre réglages.

1. Fixer HF_HOME avant tout import

Les variables HF_* sont lues au moment où huggingface_hub est importé. Un os.environ["HF_HOME"] = ... écrit après import sentence_transformers ou import transformers n'a plus aucun effet : la bibliothèque a déjà calculé ses chemins.

La règle : ces variables vont dans le Dockerfile (ENV) ou dans l'environnement du conteneur, jamais au milieu du code.

2. Savoir où atterrit le modèle

Avec HF_HOME=/app/hf_cache, le cache est rangé dans /app/hf_cache/hub/models--<org>--<nom>/. Le sous-dossier hub vient de HF_HUB_CACHE, qui vaut $HF_HOME/hub par défaut.

Le piège : le paramètre cache_folder de sentence-transformers attend le dossier qui contient directement les models--…, donc /app/hf_cache/hub. Lui passer /app/hf_cache le fait chercher un étage trop haut. Le plus simple est de ne pas passer cache_folder du tout et de laisser HF_HOME décider, au build comme à l'exécution.

3. Garder le cache hors des volumes

Si le cache est dans un dossier monté en volume au démarrage (/models chez moi), le volume masque le contenu de l'image. Le modèle téléchargé au build disparaît, et le volume arrive avec les droits de l'hôte, souvent root. Le conteneur ne peut alors ni lire l'ancien cache ni en écrire un nouveau.

Je place donc le cache dans un dossier de l'image (/app/hf_cache), qui appartient à l'utilisateur d'exécution.

4. Vérifier hors ligne pendant le build

Dernier réglage, le plus utile : recharger le modèle avec HF_HUB_OFFLINE=1 dans le Dockerfile. Dans ce mode, la bibliothèque ne fait aucun appel réseau et n'utilise que le cache. Si le modèle n'est pas exactement là où le code ira le chercher, le build échoue, et l'image cassée n'est jamais livrée.

# Cache dans l'image, hors de tout volume.
ENV HF_HOME=/app/hf_cache

# 1) Téléchargement au build.
RUN python -c "from huggingface_hub import snapshot_download; snapshot_download('BAAI/bge-m3')"

# 2) Rechargement sans réseau, exactement comme au démarrage : échec = build en échec.
RUN HF_HUB_OFFLINE=1 python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('BAAI/bge-m3')"

# 3) Le cache appartient à l'utilisateur qui lance le service.
RUN chown -R runtime:runtime /app/hf_cache   # « runtime » : à adapter

Bonus : même avec le modèle en cache, huggingface_hub envoie normalement une requête pour vérifier s'il existe une version plus récente. Avec HF_HUB_OFFLINE=1 à l'exécution, cette requête disparaît et le chargement est plus rapide. À activer si le conteneur n'a pas vocation à mettre le modèle à jour tout seul.

Source