diff --git a/src/script/build-with-container.py b/src/script/build-with-container.py
index 2cd1d4d3ad1..82f892924cb 100755
--- a/src/script/build-with-container.py
+++ b/src/script/build-with-container.py
@@ -182,10 +182,16 @@ def _container_cmd(ctx, args, *, workdir=None, interactive=False):
     if workdir:
         cmd.append(f"--workdir={workdir}")
     cwd = pathlib.Path(".").absolute()
-    cmd += [
-        f"--volume={cwd}:{ctx.cli.homedir}:Z",
-        f"-eHOMEDIR={ctx.cli.homedir}",
-    ]
+    overlay = ctx.overlay()
+    if overlay and overlay.temporary:
+        cmd.append(f"--volume={cwd}:{ctx.cli.homedir}:O")
+    elif overlay:
+        cmd.append(
+            f"--volume={cwd}:{ctx.cli.homedir}:O,upperdir={overlay.upper},workdir={overlay.work}"
+        )
+    else:
+        cmd.append(f"--volume={cwd}:{ctx.cli.homedir}:Z")
+    cmd.append(f"-eHOMEDIR={ctx.cli.homedir}")
     if ctx.cli.build_dir:
         cmd.append(f"-eBUILD_DIR={ctx.cli.build_dir}")
     if ctx.cli.ccache_dir:
@@ -356,6 +362,29 @@ class Context:
         except DidNotExecute:
             pass
 
+    def overlay(self):
+        if not self.cli.overlay_dir:
+            return None
+        overlay = Overlay(temporary=self.cli.overlay_dir == "-")
+        if not overlay.temporary:
+            obase = pathlib.Path(self.cli.overlay_dir).resolve()
+            # you can't nest the workdir inside the upperdir at least on the
+            # version of podman I tried. But the workdir does need to be on the
+            # same FS according to the docs.  So make the workdir and the upper
+            # dir (content) siblings within the specified dir. podman doesn't
+            # have the courtesy to manage the workdir automatically when
+            # specifying upper dir.
+            overlay.upper = obase / "content"
+            overlay.work = obase / "work"
+        return overlay
+
+
+class Overlay:
+    def __init__(self, temporary=True, upper=None, work=None):
+        self.temporary = temporary
+        self.upper = upper
+        self.work = work
+
 
 class Builder:
     """Organize and manage the build steps."""
@@ -373,6 +402,8 @@ class Builder:
         if step in self._did_steps:
             log.info("step already done: %s", step)
             return
+        if not self._did_steps:
+            prepare_env_once(ctx)
         self._steps[step](ctx)
         self._did_steps.add(step)
         log.info("step done: %s", step)
@@ -395,6 +426,14 @@ class Builder:
             yield str(step), getattr(func, "__doc__", "")
 
 
+def prepare_env_once(ctx):
+    overlay = ctx.overlay()
+    if overlay and not overlay.temporary:
+        log.info("Creating overlay dirs: %s, %s", overlay.upper, overlay.work)
+        overlay.upper.mkdir(parents=True, exist_ok=True)
+        overlay.work.mkdir(parents=True, exist_ok=True)
+
+
 @Builder.set(Steps.DNF_CACHE)
 def dnf_cache_dir(ctx):
     """Set up a DNF cache directory for reuse across container builds."""
@@ -745,6 +784,15 @@ def parse_cli(build_step_names):
             " (the ceph source root)"
         ),
     )
+    parser.add_argument(
+        "--overlay-dir",
+        "-l",
+        help=(
+            "Mount the homedir as an overlay volume using the given dir"
+            "to host the overlay content and working dir. Specify '-' to"
+            "use a temporary overlay (discarding writes on container exit)"
+        ),
+    )
     parser.add_argument(
         "--ccache-dir",
         help=(