Skip to content

190 cleanup extra docker images created by mlflow - #191

Merged
mikewoodward94 merged 5 commits into
mainfrom
190-cleanup-mlflow-run-docker-image
Oct 5, 2026
Merged

mikewoodward94 merged 5 commits into
mainfrom
190-cleanup-mlflow-run-docker-image

Conversation

@mikaelsimard5

Copy link
Copy Markdown
Collaborator

Explanation of this bug

Closes #190.

mlops run left Docker images behind on every run, which shows up as a growing list of <none>:<none> images (bug #190).

General context on what happens: when you do mlops run there are always two images that are created/accessed; the per-project image, which is tagged as <name>:latest, and contains whatever the project's Dockerfile builds (mostly the OS, system and pip packages). Then, mlflow creates a second image (run-specific image rather than project), tagged as <name>:<first 7 chars of git commit>, which contains the code.

After investigating here's what creates a bunch of extra images tagged as <none>:<none>:

(1) the per-run mlflow image is never removed. If you do a first run (run A) then you'll have two images, <name>:latest and <name>:<first 7 chars of git commit>. If you do a second run on the same commit (run B), you'll end up with three images: the same <name>:latest as before, and then what happens is that the per-run mlflow image from run A has its tag moved to the per-run mlflow image of run B. Result is the per-run image of run B now has the <name>:<first 7 chars of git commit> tag, and the per-run image of run A has "lost" its tag and shows <none>:<none>.

(2) The old project image persists after rebuild_docker (-r). Rebuilding <name>:latest moves the tag to the new image and leaves the previous one as <none>:<none>, so similar mechanism as above but on the per-project image. This happens on Docker's classic image store, e.g. Linux servers.

Fixes

The changes below fix the issues so that we're always left with a single image (the project one, <name>:latest) as there is no need to preserve the other one.

(1) per-run image cleanup: mlflow.run is now wrapped in try/finally, and remove_run_image() removes <name>:<sha7> after every run, including failed or interrupted ones.
(2) rebuild cleanup: after a rebuild, remove_replaced_image() removes the previous <name>:latest image, but only if it has no name left.

Possible issue

The <name>:<sha7> tag format is internal to mlflow. I checked it against mlflow 2.0.0, 3.0.0, every release from 3.11.1 to 3.16.1, and current master. It's identical in all of them. If a future mlflow changes it, the cleanup logs a warning, and test_run would fail in CI so we would notice.

Testing

  • New tests: test_remove_run_image and test_remove_replaced_image. test_run now also asserts that only <name>:latest remains after a run.

Updated the run() method in a try/finally statement that cleans the per-run image, with appropriate logging.
test_remove_run_image proves the method removes the right name and never touches the project image.

test_run -> now also checks if the try/finally statement calls the cleanup and that mlflow really builds and named its image the way we expect it.
If rebuilding docker image, then remove the previous one as it will be left here as <none>:<none> after the rebuild. That's an extra case that could leave old images around.
@mikaelsimard5 mikaelsimard5 added the bug Something isn't working label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
mlops
   Experiment.py1864277%22, 51–56, 79, 99, 101, 106, 160–171, 202, 204–205, 255–259, 263–264, 273–274, 277–282, 317–318, 327–328, 348–349, 356–363
   ProjectFile.py23196%41
   cli.py551769%19–22, 36, 59–66, 86–88, 96–99, 107–108, 113
mlops/release
   Release.py33779%24, 28, 33–38
mlops/release/destinations
   ReleaseDestination.py9189%18
   SharepointDestination.py6267%12–13
   ZenodoDestination.py331942%21–25, 29–35, 43–60
mlops/release/sources
   MLFlowSource.py13654%13–14, 21–27
   ReleaseSource.py14286%14, 23
mlops/utils
   Config.py15150%1–22
TOTAL42411274% 

Tests Skipped Failures Errors Time
18 0 💤 0 ❌ 0 🔥 30m 24s ⏱️

@mikewoodward94
mikewoodward94 self-requested a review October 5, 2026 15:31

@mikewoodward94 mikewoodward94 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent investigative work and fix, LGTM!

(so happy this is finally fixed)

@mikewoodward94
mikewoodward94 merged commit dc8c513 into main Oct 5, 2026
2 checks passed
@mikewoodward94
mikewoodward94 deleted the 190-cleanup-mlflow-run-docker-image branch October 5, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: mlops run leaves behind hanging docker image

2 participants