Docker Rootless Mode, or Fixing Persistent Docker Daemon Failure in WSL 2
This comprehensive tutorial addresses the complex and persistent issue of the Docker daemon failing to start with a "Device or resource busy" error when running a native Docker Engine inside a WSL 2 (Windows Subsystem for Linux 2) distribution, ultimately leading to the necessity of switching to Docker Rootless Mode.
1. The Problem Overview
The core issue is the native system-wide Docker daemon (dockerd) failing to initialize upon startup inside the WSL 2 environment. This failure manifests as a persistent loop of errors:
- High-Level Status:
Active: failed (Result: exit-code)orStart request repeated too quickly. - Access Block: Attempts to clear corrupted storage often fail with
mv: cannot move '/var/lib/docker': Device or resource busy. - Root Cause: The failures stem from a combination of stale lock files (
docker.pid), corrupted storage metadata (metadata.db), and fundamental conflicts with the WSL 2 kernel's implementation of features like Cgroups or network socket activation.
To resolve this reliably, the solution is to bypass the system-level conflicts by switching from the problematic rootful daemon to the more stable Docker Rootless mode.
2. Step-by-Step Resolution
The resolution involves three phases: diagnosing the specific low-level crash, performing an aggressive cleanup to free the lock, and finally, installing the stable rootless solution.
Phase 1: Aggressive Cleanup and File Lock Removal
The persistent "Device or resource busy" error is the primary block. Even a full Windows reboot or wsl --shutdown often fails to clear the lock held on /var/lib/docker.
A. Forcefully Shut Down WSL 2
- Close all WSL terminals.
- Open Windows PowerShell (or CMD).
- Execute the global shutdown command: This ensures the Linux kernel and all running processes are terminated, releasing file locks.
wsl --shutdown
B. Identify and Rename the Corrupted Directory
- Relaunch your WSL terminal.
- Rename the Corrupted Docker Storage: This creates a fresh start for the storage driver. If this fails with
Device or resource busy(which is highly likely), proceed to step C.sudo mv /var/lib/docker /var/lib/docker.bak -
[If Rename Fails] Terminate and Delete the Lock File: The daemon failed because it was locked by a rogue PID, which often leaves behind a stale PID file.
# Stop the failing service (just in case it auto-started) sudo systemctl stop docker.service # Delete the stale PID file that falsely signals the daemon is running sudo rm /var/run/docker.pid
Phase 2: Switch to Docker Rootless Mode
Rootless mode installs the daemon under your standard user account, isolating it from the system-level issues that caused the failure.
A. Install Prerequisites
Install the uidmap package, which is necessary for managing user namespaces in the rootless environment.
- Check and clear any package locks (if necessary):
Ifsudo apt installhangs, check for and kill the conflicting process (e.g.,unattended-upgrusingsudo kill -9 <PID>), and then delete the lock files:sudo rm /var/lib/dpkg/lock-frontend sudo rm /var/lib/dpkg/lock sudo dpkg --configure -a - Install
uidmap:sudo apt update sudo apt install uidmap
B. Install the Rootless Daemon
- Ensure the system-wide daemon is stopped and disabled to prevent conflicts:
sudo systemctl stop docker.service sudo systemctl disable docker.service sudo rm /var/run/docker.sock # Clean up the system socket - Run the Rootless setup script:
dockerd-rootless-setuptool.sh install
Phase 3: Configure and Launch
The setup script completes the installation but requires manual configuration to launch the daemon and set the necessary environment variables.
A. Configure Shell Environment
- Edit your bash profile (
~/.bashrc):vi ~/.bashrc - Add the necessary environment variables (these lines are typically provided by the setup script and redirect the client to the rootless socket):
# Docker Rootless configuration for user <your_username> export XDG_RUNTIME_DIR=/home/<your_username>/.docker/run export PATH=/usr/bin:$PATH export DOCKER_HOST=unix:///home/<your_username>/.docker/run/docker.sock - Save the file and exit the editor.
B. Startup Sequence (Required on Every WSL Launch)
Because your WSL environment is not using a fully managed systemd to start the rootless daemon automatically, you must execute the following two commands every time you open a new terminal:
- Source the configuration: Activates the
DOCKER_HOSTenvironment variable in the current session.source ~/.bashrc - Start the Rootless Daemon: Launches the user-level daemon in the background.
dockerd-rootless.sh &
C. Final Verification
Wait a few seconds after launching the daemon, then verify connectivity:
docker ps
The client will now connect to the stable, user-level daemon, resolving the persistent startup failures.