Skip to content

Add volume variant for persistent /home/user mount#316

Open
netbrain wants to merge 9 commits intomasterfrom
feat/minimal-variant
Open

Add volume variant for persistent /home/user mount#316
netbrain wants to merge 9 commits intomasterfrom
feat/minimal-variant

Conversation

@netbrain
Copy link
Owner

@netbrain netbrain commented Mar 12, 2026

Summary

  • Adds ZWIFT_VARIANT=volume mode where /home/user is mounted as a persistent Docker volume (zwift-home-$USER)
  • On first run, the container runtime auto-populates the volume from the image
  • Ownership persists in the volume, skipping per-launch chown for Docker users with uid/gid != 1000
  • Pre-launch sync: when the image is updated, an init container rsyncs changes into the volume while preserving user data
  • Adds zwift-volume Nix package and variant option to NixOS module
  • Adds documentation: dedicated volume variant page, updated config options, NixOS docs, and slow-start troubleshooting

How it works

Docker and Podman automatically copy image contents into a new named volume on first mount. By mounting /home/user as a volume instead of just the Zwift docs directory, the entire wine prefix persists between runs.

Volume sync on image update: Before launching, zwift.sh compares the image's OCI version label against a marker file in the volume. If they differ, it runs a short-lived container that mounts the volume at /mnt/volume (keeping the image's /home/user visible) and uses rsync to copy updated files into the volume. User data (activities, workouts, logs, preferences, screenshots) is excluded from the sync.

Files changed

File Change
src/zwift.sh Volume mount switching, pre-launch version check and rsync sync
src/entrypoint.sh Skip chown block when ZWIFT_VOLUME=1
src/update_zwift.sh Guard rm -rf $ZWIFT_DOCS behind ZWIFT_VOLUME check
src/Dockerfile Add rsync package
.github/workflows/zwift_build_from_scratch.yaml Embed ZWIFT_IMAGE_VERSION env var at commit time
flake.nix Add variant option, zwift-volume package
docs/advanced/volume-variant.md New documentation page
docs/configuration/options.md Add ZWIFT_VARIANT to options table
docs/advanced/nixos.md Add variant to NixOS config example
docs/troubleshooting/slow-start.md Mention volume variant as a solution

Test plan

  • First run: verify volume is populated and chown is skipped
  • Second run: verify it launches directly, volume marked up to date
  • Simulate image update: change version label, verify rsync triggers
  • Default variant (container) still works unchanged
Local testing instructions (no Zwift installation required)
# 1. Build the base image
docker build -t netbrain/zwift:test src/

# 2. Add a version label (simulates what CI does at docker commit time)
echo "FROM netbrain/zwift:test" \
  | docker build --label "org.opencontainers.image.version=test-1.0" \
    -t netbrain/zwift:test-labeled -

# 3. First run — volume should be populated from image
ZWIFT_VARIANT=volume IMAGE=netbrain/zwift VERSION=test-labeled \
  DONT_CHECK=1 DONT_PULL=1 ZWIFT_FG=1 INTERACTIVE=1 \
  bash src/zwift.sh

# 4. Inside the container, verify /home/user has content, then exit

# 5. Second run — should say "Volume is up to date"
ZWIFT_VARIANT=volume IMAGE=netbrain/zwift VERSION=test-labeled \
  DONT_CHECK=1 DONT_PULL=1 ZWIFT_FG=1 INTERACTIVE=1 \
  bash src/zwift.sh

# 6. Simulate image update — rebuild with a new version
echo "FROM netbrain/zwift:test" \
  | docker build --label "org.opencontainers.image.version=test-2.0" \
    -t netbrain/zwift:test-labeled -

# 7. Run again — should trigger rsync sync
ZWIFT_VARIANT=volume IMAGE=netbrain/zwift VERSION=test-labeled \
  DONT_CHECK=1 DONT_PULL=1 ZWIFT_FG=1 INTERACTIVE=1 \
  bash src/zwift.sh

# Cleanup
docker volume rm "zwift-home-$USER"

Closes part of issue 28 (variant 2: volume-based). Remaining work on issue 28: variants 3-4 (native wine).

Introduces ZWIFT_VARIANT=minimal mode where /home/user is mounted as a
persistent volume. Zwift installs on first run into the volume instead of
being baked into the container image. Eliminates per-launch chown for
Docker users with non-1000 uid/gid and reduces image size.
Instead of a separate lightweight image that installs Zwift at first run,
use the same pre-installed image with a /home/user volume mount. Docker
and Podman auto-populate new named volumes from image contents, so the
first launch is instant with no installation step needed.
@netbrain netbrain force-pushed the feat/minimal-variant branch from 197cee9 to a7f49c5 Compare March 12, 2026 08:13
@netbrain netbrain changed the title Add minimal variant for volume-based Zwift installation Add volume variant for persistent /home/user mount Mar 12, 2026
Before launching Zwift, compare the image version (OCI label) against a
marker in the volume. If they differ, run an init container that rsyncs
the image /home/user into the volume, preserving user data (activities,
workouts, logs, prefs, screenshots). This keeps the volume up to date
with the image without network downloads or image size duplication.
@netbrain
Copy link
Owner Author

This might be the way going forward, however very unsure, as it might have unforseen implications. It would allow us to run zwift in a volume, as opposed to within a container, and by default everything is persisted, as opposed to being ephemeral.

Update is somewhat trickier, but i guess we can test this approach to see how it fares over time, and if this seems to work, it might be favorable to todays solution and we might remove the ZWIFT_VARIANT=container option.

What are your thoughts @glennvl?

Copy link
Contributor

@glennvl glennvl left a comment

Choose a reason for hiding this comment

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

I have a few thoughts about this, but let's discuss those in the issue instead.

Co-authored-by: Glenn <glenn.vanloon@gmail.com>
@glennvl glennvl mentioned this pull request Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants