fix(launcher): reuse MPC container across restarts to preserve logs - #3705
Conversation
The launcher removed and recreated the MPC container on every boot (docker rm -f + docker compose up -d), dropping Docker's container-ID-keyed logs each restart. The rm -f worked around a name conflict from running as a different compose project every boot (random temp compose path, no -p). Pass an explicit `-p mpc` compose project so Compose reuses the existing container when the config is unchanged (preserving its logs) and recreates it only when the rendered config changes (e.g. image digest). No name-conflict cleanup is needed: a launcher change re-keys the CVM disk (the key is derived from the launcher's compose measurement), so a new launcher always boots on a fresh disk with no prior mpc-node container. Also note in the TDX guide that logs persist across a restart but are cleared on an upgrade.
9fa37d8 to
3e679db
Compare
Pull request overviewFixes a log-loss issue in the TEE launcher: on every boot the launcher was force-removing the Changes:
Reviewed changesPer-file summary
FindingsNon-blocking (nits, follow-ups, suggestions):
✅ Approved |
There was a problem hiding this comment.
Pull request overview
This PR updates the TEE launcher’s Docker Compose invocation to reuse the existing MPC container across restarts (so Docker’s container-ID-keyed logs persist), and documents the resulting log-retention behavior for operators using dstack/TDX.
Changes:
- Run
docker compose upunder a stable project name (-p mpc) and stop force-removing the MPC container on each boot. - Document that logs persist across restarts but reset on upgrades (container recreation due to new image digest).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| docs/running-an-mpc-node-in-tdx-external-guide.md | Adds a note clarifying restart vs upgrade log-retention behavior in the dstack log UI section. |
| crates/tee-launcher/src/compose.rs | Switches Compose to a stable project (-p mpc) and removes unconditional container deletion to preserve logs across restarts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Use a stable compose project (`-p`) so the container is reused across | ||
| // restarts rather than recreated, preserving its logs. Compose | ||
| // recreates it only when the rendered config (e.g. image digest) changes. | ||
| let run_output = Command::new("docker") | ||
| .args(["compose", "-f", &compose_path, "up", "-d"]) | ||
| .args(["compose", "-p", "mpc", "-f", &compose_path, "up", "-d"]) | ||
| .output() |
There was a problem hiding this comment.
Thanks — this is the right concern on a general Docker host, but it doesn't apply to our deployment, so the one-time cleanup is intentionally left out:
- TEE (production): the CVM disk is encrypted with a key derived from the launcher's docker-compose measurement (see
docs/securing-mpc-with-tee-design-doc.md, Launcher Pattern, step 3). A launcher change ⇒ different measurement ⇒ different key ⇒ the old disk can't be decrypted, so a new launcher always boots on a fresh disk with no pre-existingmpc-nodecontainer to collide with. - The only situation this would guard is an in-place launcher upgrade that reuses the disk (e.g. NonTee on a persistent host), which isn't a supported flow for us.
- Node-image upgrades keep the same launcher (same compose project label), so there's no conflict on that path either.
|
Thanks for the review. On the non-blocking nits — leaving the code as-is, with rationale:
|
| // recreates it only when the rendered config (e.g. image digest) changes. | ||
| let run_output = Command::new("docker") | ||
| .args(["compose", "-f", &compose_path, "up", "-d"]) | ||
| .args(["compose", "-p", "mpc", "-f", &compose_path, "up", "-d"]) |
There was a problem hiding this comment.
nit: you can instead having project name in yaml itself: name: mpc
https://docs.docker.com/reference/compose-file/version-and-name/#name-top-level-element
There was a problem hiding this comment.
Good call — done in b23ac3e: moved the project name into the compose file as a top-level name: mpc-node and dropped the -p flag, so it's declarative and now covered by the render tests. Thanks!
Set the project name declaratively as a top-level `name: mpc-node` in the rendered compose file instead of passing `-p mpc` on the command line (per review). The project identity now travels with the compose config and is covered by render tests. Behaviour is unchanged: a stable project name keeps the container reused across restarts, preserving its logs.
Closes #3695.
The launcher recreated the MPC container on every boot (
docker rm -f+docker compose up -d), wiping its logs. Run Compose under a stable project (-p mpc) and drop therm -f, so the container is reused across restarts and its logs persist — viewable via the existing dstack log interface. No name-conflict cleanup is needed: a launcher change re-keys the CVM disk, so a new launcher always boots on a fresh disk.Scope: restart only. Logs are still reset on upgrade (new image digest → container recreated) — tracked in #3702.
Verified on a dstack TDX localnet: restart → logs preserved; upgrade → logs reset.