Answering the Core Questions
1. How to keep the NuGet cache alive across container rebuilds? Mount a Docker volume (or a host‑path bind mount) at ~/.nuget/packages inside the container. The volume survives each docker‑compose up or Codespaces rebuild, so restored packages are reused without re‑downloading.
2. Recommended mapping strategy that avoids permission conflicts on Windows, Linux, macOS, x64, and ARM64 hosts? Use a non‑root user whose UID/GID match the host user. Configure remoteUser in .devcontainer.json and create the same user in the Dockerfile. Then mount the volume or host path as that user, ensuring the cache files are owned correctly on all platforms.
Step‑by‑Step Implementation
- Create the Docker volume (only once):
docker volume create nuget-cache
- Update
.devcontainer.json to mount the volume and set the user:
{
"name": ".NET Dev Container",
"remoteUser": "vscode",
"mounts": [
"source=nuget-cache,target=/home/vscode/.nuget/packages,type=volume"
],
"postCreateCommand": "dotnet restore"
}
- Adjust the Dockerfile (if custom) to create the
vscode user with matching UID/GID:
ARG USERNAME=vscode
ARG USER_UID=9000
ARG USER_GID=$USER_UID
RUN groupadd --gid $USER_GID $USERNAME && \n useradd --uid $USER_UID --gid $USER_GID -m $USERNAME
USER $USERNAME
- Set the cache location explicitly (optional but safe) by adding an environment variable in
.devcontainer.json:
"containerEnv": {"NUGET_PACKAGES": "/home/vscode/.nuget/packages"}
- Verify the setup:
- Start the container and run
dotnet restore. Note the download time and that packages appear in /home/vscode/.nuget/packages.
- Stop the container (
docker compose down – do not remove the volume). Restart it and run dotnet restore again – it should report “All packages are up to date” and finish quickly.
- Check ownership:
ls -l /home/vscode/.nuget/packages should show the vscode user.
- Cross‑architecture safety: NuGet stores packages as platform‑neutral .nupkg files; only native assets are extracted per runtime. The same cache works on x64 and ARM64 without conflict.
Key Takeaways
- Use a named volume for repeatability; it outlives container rebuilds.
- Mount the volume as a non‑root user that matches the host UID/GID to avoid permission errors on any OS.
- Optionally map a host directory (e.g.,
${localWorkspaceFolder}/.nuget) if you prefer a bind mount; just ensure the directory exists and has the right permissions.
- Set
NUGET_PACKAGES to guarantee dotnet uses the mounted cache.
- Keep the lock file (
packages.lock.json) in source control; the cache is a performance aid, not a source of truth.
What’s Still Needed?
Do you currently use remoteUser in your devcontainer, or would you prefer a host‑path bind mount? Knowing this will help fine‑tune the instructions for your workflow.