SSD Nodes Learn 🎉 VPS from $5.50/mo
Guides Matt ConnorBy Matt Connor · Updated 2026-08-19

WSL vs a VPS for development

WSL and a VPS solve different halves of a dev setup: uptime, a public address, systemd, filesystem speed, backups, and the SSH bridge between them.

Should you use WSL or a VPS for development?

WSL vs a VPS for development comes down to one property: availability. WSL (Windows Subsystem for Linux) runs Ubuntu inside a virtual machine that lives and dies with your Windows session. A VPS (virtual private server) runs the same Ubuntu on a public IP address that stays up while your laptop is closed. Most developers end up using both, with the server as the machine that stays reachable.

The operating system is not the interesting difference, because both are Ubuntu. What differs is uptime, reachability from the internet, what systemd can promise, how fast files are, how the network behaves and who holds the backups. Every section below is a difference you can watch happen on your own machine.

Why does WSL stop when you close the laptop?

WSL 2 runs a real Linux kernel in a lightweight virtual machine that Windows starts on demand. That virtual machine exists only while a distribution is running, and a distribution runs only while something is using it. Check the state from PowerShell:

wsl --version
wsl --list --running

Close every WSL terminal, wait a minute, then run wsl --list --running again. When it reports that there are no running distributions, the shell you started is gone and so is everything it was running. wsl --shutdown does the same thing immediately, which is a useful way to test how your setup behaves after a restart.

Sleep and hibernate stop the virtual machine too. A timer set to dump a database at 03:00 does not fire while the lid is closed, because the kernel that would run it is not executing. Nothing logs an error, so the job looks like it was never scheduled at all. This single behaviour is what sends people to a second machine: a build queue, a chat bot, a nightly backup or a webhook receiver all need a computer that stays on.

Does systemd work in WSL?

Yes. Support arrived in WSL 0.67.6, and it is off by default on older installs. Without it, systemctl status ssh prints:

System has not been booted with systemd as init system (PID 1). Can't operate.

Read the config file first, because you may already have one. If it has no [boot] section, append one; if it does, add the single line inside the section that is already there.

cat /etc/wsl.conf
sudo tee -a /etc/wsl.conf >/dev/null <<'EOF'
[boot]
systemd=true
EOF

Run wsl --shutdown in PowerShell, open a new Ubuntu shell, then check with systemctl list-units --type=service --state=running. A list of units means systemd is PID 1, and journalctl -b works from that point on.

The catch is what enable promises on each machine. On a VPS, sudo systemctl enable --now caddy means the service starts at boot, so it returns after a reboot or a kernel upgrade with nobody logged in. In WSL it means the service starts when the distribution starts, and the distribution starts when you open a terminal. So the service is up only while you are working, which is backwards from the reason services exist. Containers inherit the same gap, which is why making Docker Compose services start on boot depends on a boot that WSL performs only when you ask for one.

Can a webhook reach a server running in WSL?

Not without help, and the reason is the network layout. In its default mode WSL 2 puts the virtual machine behind NAT (network address translation) on its own virtual adapter. Look at the address:

ip -4 addr show eth0
ip route show default

That address is private, and it is handed out again every time the virtual machine starts, so it changes. Windows itself can still reach localhost:3000, because WSL forwards localhost connections into the distribution. Another machine on your network cannot, unless you add a proxy rule from an Administrator PowerShell:

netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=172.24.108.3

That rule names one address, so it breaks the next time the address changes. Mirrored networking is the better option: the distribution gets the same interfaces and addresses as Windows. As of August 2026 it needs Windows 11 22H2 or newer. Put this in %UserProfile%\.wslconfig and run wsl --shutdown:

[wsl2]
networkingMode=mirrored

Mirrored mode fixes the local network problem. It does not give you a public address. Your router runs NAT again, most home connections have no inbound port you control, and many ISPs add another layer of NAT above that. So GitHub cannot POST an event to your laptop, and a colleague cannot open your demo link. A tunnel service works around it, but the tunnel client runs on the laptop, which means the laptop is still the machine that has to stay awake.

A VPS starts from the other side of that problem. It has a public IPv4 address and usually a public IPv6 address, with only the ports you open. Point an A record at it, allow 80 and 443, and it answers from anywhere. That is also the condition for a public certificate, because the HTTP-01 challenge asks Let's Encrypt to fetch a file over port 80 on the public name. Getting a Let's Encrypt certificate with Certbot and nginx is a five minute job on a server and impossible in WSL. For local work you can still get browser-trusted HTTPS inside WSL by adding your own CA to the Ubuntu trust store.

Why is git slow on /mnt/c?

Because the files are not on the Linux filesystem. WSL gives you two storage areas with very different costs. Your home directory sits on an ext4 filesystem inside a virtual disk, and it behaves like an ordinary Linux disk. /mnt/c is the Windows drive, served over the 9P protocol (Plan 9 filesystem protocol) by a component on the Windows side, so every open and every stat crosses that boundary.

One file is fine. git status on a large repository makes thousands of stat calls, and each one pays the crossing. Measure it instead of trusting a number from anyone, including this page:

cd /mnt/c/Users/you/code/myrepo && time git status
cp -r /mnt/c/Users/you/code/myrepo ~/myrepo
cd ~/myrepo && time git status

Run each one twice and compare the second runs, so both are warm. Windows real-time antivirus scanning adds more cost on the /mnt/c side, which is why the same repository can feel slower on a company laptop than on a personal one.

The fix inside WSL is to keep the working copy under ~ and open it with your editor's WSL remote mode, which runs the editor server inside the distribution rather than reaching across the boundary. Explorer can still browse those files at \\wsl.localhost\Ubuntu\home\you. A VPS does not have this problem, since there is one filesystem and it is Linux. What you pay there is network latency while editing, so people work in a terminal multiplexer or a remote editor session. Shared CPU is the honest caveat on a small server, and steal time from a noisy neighbour shows up in top as the st column.

Where WSL wins outright

  • It is free and already on the machine. Turn it on, install Ubuntu, and you are working in a minute, with nothing to pay and no public surface to defend.
  • It is disposable in a way a server is not. wsl --export Ubuntu D:\wsl-backups\ubuntu.tar writes the whole distribution to one file, and wsl --import restores it or clones it under a second name. Trying a new Ubuntu release here is a clone and a rollback, where upgrading a VPS from 24.04 to 26.04 is a one way move you have to schedule around the services already running on it.
  • GPU work is direct. With a current Windows GPU driver the card is available inside the distribution, so CUDA and ROCm workloads run against hardware you already own. Renting the same class of GPU by the hour costs real money.
  • The editing loop is shorter. Your files and your browser are both local, so a dev server on localhost:5173 opens in the browser you are already signed into.

Those are real advantages, and they are why the usual answer is both machines rather than one.

Who owns the backups?

You do, on both machines, and WSL is where that surprises people. The distribution is a virtual disk file (ext4.vhdx) inside your Windows user profile. No provider snapshots it for you. wsl --unregister Ubuntu deletes it with no undo, and a Windows reinstall takes it along with everything else. Export it on a schedule you will actually keep:

wsl --export Ubuntu D:\wsl-backups\ubuntu-2026-08-18.tar

On a VPS the provider snapshot protects you against a dead host. It does not protect you against rm -rf in the wrong directory, and a snapshot held in the same account as the server is one stolen login away from being gone with it. Push file level backups off the box, and restore one before you need to. Responsibility sits with you in both cases. The practical difference is that a server can push its own backup at 03:00 without asking anybody to leave a laptop open.

The bridge: SSH from WSL to the VPS

A second machine feels like a chore only until the connection is set up properly. Do this once inside WSL.

Generate the key in the distribution rather than on the Windows side, so the private key stays on the ext4 filesystem with Unix permissions that ssh accepts:

ssh-keygen -t ed25519 -C "dev@laptop"
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@203.0.113.10

Ed25519 keys are short and fast, and ssh-copy-id appends the public key to ~/.ssh/authorized_keys on the server with the correct modes. SSH key management basics covers rotating and revoking them later.

Give the server a name in ~/.ssh/config:

Host dev
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes
  ForwardAgent yes
  ServerAliveInterval 30

Now ssh dev connects. IdentitiesOnly yes stops the client offering every key it holds, which is what produces Too many authentication failures when an agent has several loaded. ServerAliveInterval 30 keeps a session on a home connection from dying without a message.

ForwardAgent yes is the line that makes the workflow painless. With your key loaded in the agent on the laptop, git clone git@github.com:you/app.git works on the server without a private key ever landing there. Test it with ssh -T git@github.com from the VPS, which should answer Hi you! You've successfully authenticated. Forward the agent only to servers you trust, because root on that machine can use your agent socket while you are connected. On a server you share with other people, a per repository deploy key is the safer choice.

WSL does not keep an agent alive between shells, so every new terminal asks for the key again. keychain solves that:

sudo apt update && sudo apt install -y keychain
echo 'eval "$(keychain --eval --quiet id_ed25519)"' >> ~/.bashrc

Open a new shell and run ssh-add -l. It should print the key fingerprint. Error connecting to agent instead means the line is not being read, so check that your shell actually sources ~/.bashrc.

Run the work on the server inside a terminal multiplexer, so a dropped connection does not kill it:

tmux new -s dev
# Ctrl-b then d to detach
tmux attach -t dev

The build keeps going while you close the laptop, which is the entire reason for the second machine. The same pattern is how people run Claude Code on a VPS in tmux and pick the session back up from another device.

Harden the box before you put anything on it. The first ten minutes on a new VPS walks through the non-root user, key-only SSH, a firewall and automatic security updates, in the order that does not lock you out.

Which machine for which job?

Use WSL when the work is on your screen. Editing, running the test suite, a dev server on localhost, notebooks, GPU experiments, anything you start and then watch.

Use the VPS when the work has to be reachable or has to outlive your session. A staging URL a client can open, a webhook endpoint, a cron job with a real clock, a bot, a small database another service talks to, an import you start on Friday afternoon.

If you are still deciding what the second machine is for, the things people actually run on a VPS is a more useful list than a specification comparison, and what a VPS is explains the virtualisation underneath it. If your tooling only runs on Windows, that is a separate decision, and Linux compared with Windows Server is the page for it.

One habit keeps two machines from becoming two half configured machines: the code lives in git, and both machines are clients of the repository. Nothing important exists in only one of them.

FAQ

Can I host a website with a real domain from WSL?

Not reliably. WSL 2 sits behind NAT inside your machine, your router runs NAT again, and most home connections give you no inbound port to forward. A tunnel service can expose a local port, but the tunnel client runs on the laptop, so the site is down whenever the laptop sleeps. Certificates make it harder still, because the HTTP-01 challenge requires Let's Encrypt to fetch a file over port 80 on the public name. A VPS with a public IP address and an A record meets both conditions with no workaround.

Does systemctl enable work in WSL?

It works once systemd is on, which means systemd=true under [boot] in /etc/wsl.conf followed by wsl --shutdown. Without it, systemctl replies System has not been booted with systemd as init system (PID 1). Can't operate. Even with systemd running, enable starts the service when the distribution starts, and the distribution starts when you open a shell. On a server the same command means the service comes back after a reboot with nobody logged in.

Why does my WSL IP address keep changing?

In the default NAT mode the virtual machine receives a fresh private address from the WSL virtual adapter each time it starts. Any netsh interface portproxy rule or hard-coded address stops working after wsl --shutdown. Check the current one with ip -4 addr show eth0. Mirrored networking mode on Windows 11 removes the separate address by giving the distribution the same interfaces as Windows: set networkingMode=mirrored under [wsl2] in %UserProfile%\.wslconfig.

Is /mnt/c really slower, or is that a myth?

It is slower, and one minute of testing proves it on your own machine. Files under ~ live on an ext4 virtual disk. Files under /mnt/c are served over the 9P protocol by a Windows-side component, so each stat call crosses the boundary and git status over a large tree makes thousands of them. Copy the repository to ~, run time git status twice in each location, and compare the warm runs. Keep working copies under ~ and use your editor's WSL remote mode.

Do I still need WSL once I have a VPS?

Most people keep both. WSL is free and it starts instantly, so it stays the place you edit and test, and GPU work belongs there as well. The server is the machine that stays up: it holds the public name and runs the jobs that must survive a closed laptop. Keep the code in git and treat both as clients of the repository, and moving work between them costs nothing.

#wsl#ubuntu#development#vps#workflow