Sign in
← Linux on Windows: WSL
20 min · intro · Lesson 2 of 2

WSL: everyday work and gotchas

Where to keep your files, why a project in a Windows folder is slow, how to open the code in an editor, and what to do about permissions.

You've already installed WSL. Next come the questions you run into in the first week: where to keep a project, why git status thinks for five seconds, how to open code in an editor, and where that required file not found error on a script that is definitely on disk comes from.

Why this matters

Almost every beginner problem in WSL comes from one place. Two operating systems live side by side, each with its own file system, permissions, and line endings. As long as you keep them separate in your head, everything works. Mix them up, and you get slowdowns, a chmod that "disappears," and scripts that won't run.

Two file systems

After installing, you have two different storage areas.

The Linux file system lives inside WSL. Its home directory is /home/<name>, also ~. It's a fast disk, with real permissions.

Windows drives are mounted under /mnt. Drive C: shows up as /mnt/c, drive D: as /mnt/d. You can read and write there, but it comes at a cost. If you see /mnt/ in a path, you're working on a Windows drive.

pwd
/home/anna

Keep projects in Linux, not in /mnt/c

Access to /mnt/c goes through a layer between Linux and Windows. On a single file the difference is invisible. On a project with thousands of small files it turns into torture: git status thinks for seconds, installing dependencies takes much longer, builds barely crawl. Microsoft's own docs recommend it plainly: if you work in a Linux terminal, keep the files on the WSL file system.

Keep your working files in ~:

mkdir -p ~/projects
cd ~/projects
git clone https://github.com/<owner>/<repo>.git

Leave /mnt/c for exchange: grabbing a downloaded file from "Downloads", putting a report on the desktop.

cp /mnt/c/Users/<windows_name>/Downloads/report.pdf ~/

Reaching Linux files from Windows

Files inside Linux aren't hidden. The fastest way to open the current folder in File Explorer:

explorer.exe .

The trailing dot is required: it means "the current folder". You can also type \\wsl$ into File Explorer's address bar (on newer Windows builds the same thing lives at \\wsl.localhost). You'll find every installed distro there, then a path like \\wsl$\Ubuntu\home\<name>. You can pin the folder to "Quick access".

Editor

You don't need to install an editor inside Linux. VS Code can work this way: the window stays in Windows, and all the work happens inside WSL.

Install VS Code on Windows and the WSL extension (ms-vscode-remote.remote-wsl). Then from a WSL terminal, inside the project folder:

code .

On first launch, VS Code downloads its server component into WSL. A label like WSL: Ubuntu appears in the bottom-left corner of the window. It means the built-in terminal, debugger, and extensions are running on the Linux side.

Permissions on Windows drives

Files on /mnt/c don't have permissions in the usual sense. Windows keeps access its own way, and by default WSL translates the Windows user's permissions into rwx bits, the same for owner, group, and others. In your own folders, where you have full access, that shows up as -rwxrwxrwx on every file.

Here's the catch: chmod on drive C does nothing by default. The command runs without an error, but ls -l shows the same permissions as before it. The one exception: if you remove all write permissions from a file, Windows sets its "Read-only" attribute.

Real permissions on /mnt/c turn on with the metadata option in /etc/wsl.conf:

[automount]
options = "metadata"

After that you need to restart WSL (wsl --shutdown from PowerShell). It's simpler and more reliable to keep projects in ~: permissions are real there, and git sees them the way it should.

This matters for git too. If you ran chmod +x deploy.sh in ~, git records the file in the commit with mode 100755, and the script stays executable for your colleagues too. On /mnt/c there's no such guarantee.

Line endings

Windows ends a line with two characters, \r\n (CRLF), Linux with one, \n (LF). Open a script in a Windows editor, save it, and an extra \r shows up at the end of every line.

For shell scripts this breaks everything. The line #!/bin/bash turns into #!/bin/bash\r, and the system looks for an interpreter with that exact name. The error text depends on where and how you run it:

bash: ./deploy.sh: cannot execute: required file not found

That's what bash 5.2 on Ubuntu 24.04 prints. Older bash (3.2 on macOS, for example) is clearer:

bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory

^M here is the \r. And if the first line is #!/usr/bin/env bash, the error is about env:

/usr/bin/env: 'bash\r': No such file or directory

Running it as bash deploy.sh skips the first line, but \r stays at the end of every command and every value. The script seems to work, yet creates folders with an invisible character in the name. That's worse than a plain error.

You can see the \r in a file like this:

od -c deploy.sh | head -3
0000000   #   !   /   b   i   n   /   b   a   s   h  \r  \n   e   c   h
...

To remove \r, use the dos2unix utility (installed separately, sudo apt install dos2unix) or the standard tr, which strips characters from a stream. Keep in mind that if the new file is written next to the old one and then renamed over it, it'll have its own execute bit, not the old one's.

How git helps with line endings

To keep CRLF out of the repo entirely, you set up git once in WSL:

git config --global core.autocrlf input

With input, git turns CRLF into LF in what goes into the commit on git add, and adds nothing when checking files out. The catch: the file in the working copy doesn't change. git only warns:

warning: in the working copy of 'deploy.sh', CRLF will be replaced by LF the next time Git touches it

The script in the working copy stays broken until you fix it yourself or check it out again from the commit.

The core.autocrlf setting lives on your machine. For the rule to work for the whole team, it goes into the repo, in a .gitattributes file:

*.sh text eol=lf

This line tells git: .sh files are text, they always get LF in the working copy, no matter who checks the repo out. To check which rule applies to a file, run git check-attr eol -- <file>.

How much memory WSL takes

WSL2 runs in a virtual machine and by default can use up to half of the Windows RAM. If Windows starts lagging, rein it in. Create a file on Windows at C:\Users\<name>\.wslconfig (that's %UserProfile%\.wslconfig):

[wsl2]
memory=4GB
processors=2

Then restart the subsystem from PowerShell:

wsl --shutdown

The settings apply only once the WSL virtual machine has fully stopped. Just closing the terminal window leaves it running for about 8 more seconds, so wsl --shutdown is more reliable. You can change the same thing in the "WSL Settings" app from the Start menu.

systemd

Many tutorials online assume systemd, meaning commands like systemctl start docker. In the current Ubuntu that wsl --install sets up, systemd is already on. Check it:

systemctl status

If systemctl doesn't list services and instead complains that the system wasn't started with systemd, your distro is a different or older one. Then open /etc/wsl.conf with sudo nano /etc/wsl.conf and add:

[boot]
systemd=true

Then wsl --shutdown from PowerShell and start it again.

Common mistakes

What you'll need for the tasks

Self-check

Type the command or key line. Case and spaces don't matter.

1. Go to the Linux user's home directory (the short form).
2. The Windows C: drive is visible inside WSL. Go to its root.
3. Open the current directory in VS Code right from the WSL terminal.

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words and walks you through 2 labs for this lesson. This track is free.

Connect an agent

Tell the agent: use MCP izi, give me lesson wsl-setup-02-daily-work