iTranslated by AI
Turning WSL into a Sandbox Environment
Origin
When using sandboxes with coding agent CLI tools, Windows is a second-class citizen.
What other environments can achieve instantly with command-line options, you end up doing with Docker on Windows.
In many cases, official container images are provided, but since C# projects are rarely included, you often have to build your own.
Until recently, I was doing things the hard way for C#-related work, but with the high frequency of CLI tool updates, rebuilding container images every time—which takes a significant amount of time—became a hassle.
Abandoned: Updating Docker Sequentially
Since it's tedious to update the image settings and rebuild every time something like uv for MCP is needed, I thought, "Why not just commit the work on Docker every time?"
However, the terminal experience was simply too poor to be usable... (no colors, arrow keys inputting ANSI sequences directly, etc.). Additionally, the process took twice as long due to the installation itself plus the time for persistence via commit. It was quite a letdown.
Batch Command
There might be a better way to do this, though.
@echo off
:: Variable settings
set IMAGE_NAME=ubuntu
set CONTAINER_NAME=my_ubuntu_container
set NEW_IMAGE_NAME=ubuntu:latest
:: Remove old container if it exists
docker rm -f %CONTAINER_NAME% || call :ERROR
:: Start container interactively (open bash shell)
docker run --name %CONTAINER_NAME% -it %IMAGE_NAME% /bin/bash -c ^
"if ! id -u newuser &>/dev/null; then useradd -m newuser && echo 'newuser:newpassword' | chpasswd && usermod -aG sudo newuser; fi && su - newuser" ^
|| call :ERROR
:: Once work inside the container is finished, commit changes as a new image
docker commit %CONTAINER_NAME% %NEW_IMAGE_NAME% || call :ERROR
:: Remove container
docker rm %CONTAINER_NAME% || call :ERROR
:: Commit success message
echo Committed container changes as "%NEW_IMAGE_NAME%".
exit /b
:ERROR
echo.
echo ======= ERROR =======
echo.
exit 310
Operating with Docker is quite harsh, and the AI's DIFF output being monochrome completely ruins the experience.
Sandboxing WSL
For many, the WSL environment might be used almost exclusively for running coding agent CLI tools. And WSL is also used as the foundation for Docker for Windows.
If that's the case, why not just turn WSL itself into a sandbox environment instead of using Docker?
And that's what this is 👇.
Overview
- You can do whatever you want inside the WSL environment (even
rm -rf /is fine). - However, access from within WSL to the host-side drives is blocked.
-
Even if the agent runs
rm -rf /, only the WSL environment gets destroyed.
-
Even if the agent runs
- The folder from which the batch is launched is mounted as
~/<launch-folder-name>with "read-write" access.- Therefore, if the AI executes
rm -rf /, data will be lost (this is unavoidable).
- Therefore, if the AI executes
- All work done on WSL, such as
npm i -g ..., is persisted.- No need to rebuild images every time you make a change.
Startup Script
- This is a PowerShell script, but save and run it as a
.batfile. - CRLF line endings are more stable.
@echo off
:: ========================================================
:: Polyglot Script
:: Invoke PowerShell in the BAT section
::
:: [Note] Line endings must be "CRLF"!!
::
:: ========================================================
powershell -NoLogo -NoProfile -Command ^
"$s = Get-Content '%~f0'; $i = $s.IndexOf('###__POWERSHELL__###'); Invoke-Expression ($s[($i+1)..($s.Length-1)] -join \"`n\")"
exit /b
###__POWERSHELL__###
# ========================================================
# PowerShell Version: WSL Mount Script
# ========================================================
#
#
# MUST PLACE A BLANK LINE AFTER ALL JAPANESE COMMENTS
#
# !!!!!!! IT'S WEIRD BUT ***CANNOT*** OMIT IT !!!!!!!
#
#
# Get current directory
$WINPWD = Get-Location
Write-Host "[INFO] Current PowerShell directory: $WINPWD"
# Extract only the current directory name
$FOLDERNAME = Split-Path $WINPWD -Leaf
Write-Host "[INFO] Folder name for WSL mount: $FOLDERNAME"
# Check for spaces (Do not fix this as it absolutely cannot handle folders with spaces!!)
if ($FOLDERNAME -match '\s') {
Write-Host "[ERROR] Folder name contains spaces: '$FOLDERNAME'" -ForegroundColor Red
exit 1
}
# Convert Windows path \ to /
$USERPROFILE_SLASH = $env:USERPROFILE -replace '\\','/'
$WINPWD_SLASH = $WINPWD.Path -replace '\\','/'
Write-Host "[INFO] USERPROFILE with / : $USERPROFILE_SLASH"
Write-Host "[INFO] Current dir with / : $WINPWD_SLASH"
# Calculate drive letter, user path, and mount destination on WSL
$DRIVELETTER = $USERPROFILE_SLASH.Substring(0,1).ToLower()
$USERPATH = $USERPROFILE_SLASH.Substring(2)
$MNT_WINHOME = "/mnt/$DRIVELETTER$USERPATH"
# Cannot use ~ (can be used, but don't!) * Escape $HOME and expand $FOLDERNAME
$MNT_WINPWD = "`$HOME/$FOLDERNAME"
Write-Host "[INFO] Mount paths:"
Write-Host " UserProfile: $MNT_WINHOME"
Write-Host " WorkspaceDir: $MNT_WINPWD"
# ========================================================
# Check automount settings in /etc/wsl.conf
# ========================================================
Write-Host "[INFO] Checking if [automount] section exists in /etc/wsl.conf..."
# Don't delete the newline before [automount]! Don't touch the indentation as it's written directly to the file!
$wslCheck = @"
echo ---
cat /etc/wsl.conf
echo ---
if ! grep -q '^\s*\[automount\]\s*$' /etc/wsl.conf 2>/dev/null; then
sudo tee -a /etc/wsl.conf >/dev/null <<EOF
[automount]
enabled=false
mountFsTab=true
options=metadata,ro
[interop]
appendWindowsPath=false
EOF
echo 'Added [automount] section. WSL needs restart...'
exit 310
fi
"@
# Use the following to mount as read-only
# [automount]
# enabled=true
# mountFsTab=false
# options=metadata,ro
$wslCheck = $wslCheck -replace "`r",""
wsl bash -ec $wslCheck
if ($LASTEXITCODE -ne 0) {
Write-Host "[INFO] Shutting down WSL to apply automount settings..."
wsl --shutdown
Write-Host ""
Write-Host ""
Write-Host "[INFO] Please run this script again to start WSL safely."
Write-Host ""
exit 1
} else {
Write-Host "[INFO] Verified automount config"
}
# ========================================================
# Start WSL: Pass variables to Bash and mount
# ========================================================
Write-Host "[INFO] Starting WSL with isolated mount namespace..."
$wslScript = @"
#
# Don't touch the quotes! They aren't inconsistent; they are all changed intentionally!!
#
# Unmount process for all automount directories (escaped with \$dir)
#for dir in /mnt/{a..z}; do
# if [[ \`$dir != /mnt/c ]]; then
# if mountpoint -q "\`$dir"; then
# echo [INFO] Unmounting \`$dir
# sudo umount --lazy "\`$dir"
# fi
# fi
#done
### Mount Windows profile as read-only
#if ! mountpoint -q '$MNT_WINHOME'; then
# echo '[INFO] Mounting $USERPROFILE_SLASH to $MNT_WINHOME as read-only'
# sudo mkdir -p '$MNT_WINHOME'
# sudo mount -t drvfs '$USERPROFILE_SLASH' '$MNT_WINHOME' -o ro,metadata
#else
# echo '[INFO] $MNT_WINHOME is already mounted'
#fi
### Mount the current directory
#
# Don't wrap $MNT_WINPWD in quotes! Assuming it's possible will cause problems in polyglot scripts! Don't do it!!
#
if ! mountpoint -q $MNT_WINPWD; then
echo '[INFO] Mounting $WINPWD_SLASH to $MNT_WINPWD'
mkdir -p $MNT_WINPWD
sudo mount -t drvfs '$WINPWD_SLASH' $MNT_WINPWD -o metadata
else
echo '[INFO] $MNT_WINPWD is already mounted'
fi
### Start WSL
echo
echo '[TIP] How to mount windows folder'
echo ' * mkdir wsl/path'
echo ' * sudo mount -t drvfs Windows/Path wsl/path [-o [ro,]metadata]'
echo
cd $MNT_WINPWD
exec bash -l
"@
$wslScript = $wslScript -replace "`r",""
wsl bash -ec $wslScript
Colors work properly, and command history appears with the up arrow key. (As expected)
Wonderful!
Details
When you launch the batch, it first attempts to auto-update /etc/wsl.conf on WSL.
Only if
[automount]is not found
# Add the following
[automount]
enabled=false
mountFsTab=true
options=metadata,ro
[interop]
appendWindowsPath=false
After that, it mounts the Windows-side "batch launch folder (i.e., cwd/pwd)" into WSL and starts bash -l.
Miscellaneous WSL-related Info
Checking the WSL Environment
How to check the WSL sandbox environment (WSL has versions 1 and 2):
wsl --install
wsl --update
wsl --set-default-version 2
wsl --version
wsl --list --verbose
Mounting Additional Host Folders
Configure dotfolders for AI agents, etc., to be automatically mounted at startup.
Open the editor with sudo nano /etc/fstab:
-
metadatais an option that handles Linux-side permissions (chmod) effectively. -
rostands for read-only.
# Settings to mount a Windows folder as read-only inside WSL
# (Spaces are escaped with \040)
# <file system> <mount point> <type> <options> <dump> <pass>
C:/John\040Doe/Docs /mnt/documents drvfs defaults,ro,metadata 0 0
How to Verify Settings
Syntax check for fstab (WSL):
sudo mount -a
If there are no issues, exit from WSL and run the following in CMD/PowerShell:
wsl --shutdown
to restart all WSL instances.
After restarting, launch the script mentioned above to return to WSL, then run:
findmnt
to check the ro, rw, etc., status of the mounted drives.
Conclusion
While using Docker and similar tools is likely the standard approach, WSL (Hyper-V?) is the technology that even Docker for Windows is built upon.
So (while it may be a bit of a shortcut), this isn't just some pseudo-sandbox environment. I believe it allows you to run AI agents on autopilot in a much more reliable state than simply winging it because it's a hassle.
That's all. Thank you.
Discussion