Update Windows "host" File With WSL IP On Start Up
I have been able to get what I wanted thanks to help from this answer, but I did make some modifications.
Basically, I created an update_wsl_ip_to_domain.ps1
file and saved to this PATH C:\Scripts\update_wsl_ip_to_domain.ps1
, then I added the following scripts
#
# Powershell script for adding/removing/showing entries to the hosts file.
#
# Known limitations:
# - does not handle entries with comments afterwards ("<ip> <host> # comment")
#
$file = "C:\Windows\System32\drivers\etc\hosts"
$wsl_ip = (wsl -d debian hostname -I).trim()
$data = @('add','localwsl.com',$wsl_ip)
function add-host([string]$filename, [string]$hostname, [string]$ip) {
remove-host $filename $hostname
$ip + "`t`t" + $hostname | Out-File -encoding ASCII -append $filename
}
function remove-host([string]$filename, [string]$hostname) {
$c = Get-Content $filename
$newLines = @()
foreach ($line in $c) {
$bits = [regex]::Split($line, "\t+")
if ($bits.count -eq 2) {
if ($bits[1] -ne $hostname) {
$newLines += $line
}
} else {
$newLines += $line
}
}
# Write file
Clear-Content $filename
foreach ($line in $newLines) {
$line | Out-File -encoding ASCII -append $filename
}
}
function print-hosts([string]$filename) {
$c = Get-Content $filename
foreach ($line in $c) {
$bits = [regex]::Split($line, "\t+")
if ($bits.count -eq 2) {
Write-Host $bits[0] `t`t $bits[1]
}
}
}
try {
if ($data[0] -eq "add") {
if ($data.count -lt 3) {
throw "Not enough arguments for add."
} else {
add-host $file $data[1] $data[2]
}
} elseif ($data[0] -eq "remove") {
if ($data.count -lt 2) {
throw "Not enough arguments for remove."
} else {
remove-host $file $data[1]
}
} elseif ($data[0] -eq "show") {
print-hosts $file
} else {
throw "Invalid operation '" + $data[0] + "' - must be one of 'add', 'remove', 'show'."
}
} catch {
Write-Host $error[0]
Write-Host "`nUsage: hosts add <ip> <hostname>`n hosts remove <hostname>`n hosts show"
}
Next, I created a separate update_wsl_ip_to_domain.cmd
file within the same directory as the ps1
file and I added the following commands.
PowerShell Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Powershell -File C:\Scripts\update_wsl_ip_to_domain.ps1
PowerShell Set-ExecutionPolicy Restricted
So in order to get the update_wsl_ip_to_domain.cmd
file to run on startup, I created a shortcut to this directory C:\Users\<user_name>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\
and gave it admin privileges.
Finally, I also added another shortcut of the update_wsl_ip_to_domain.cmd
file on my desktop with admin privileges also, which I run manually, because for some reason the previous one doesn't always run on startup.
UPDATES
On my Debian
WSL
distro, I thought of running apache
on port 80 and nginx
on port 81.
So in order to give them a static IP and a domain name I've edited my update_wsl_ip_to_domain.ps1
file and added the following code at the bottom.
#GIVING WSL NGINX A STATIC IP. IT RUNS AT PORT 81 ON MY DEBIAN
netsh interface portproxy add v4tov4 listenport=80 listenaddress=127.65.43.21 connectport=81 connectaddress=$wsl_ip
#GIVING WSL APACHE A STATIC IP. IT RUNS AT PORT 80 ON MY DEBIAN
netsh interface portproxy add v4tov4 listenport=80 listenaddress=127.65.43.22 connectport=80 connectaddress=$wsl_ip
Then I made the following entries to my hosts
file on Windows
10.
127.65.43.21 localwslnginx.com
127.65.43.22 localwslapache.com
I believe that there is a lot of potential to this, I just might be able to run multiple apps on different ports with my apache
or nginx
.
I will make updates as I go on.
UPDATES 2 (28th, December 2021)
I have been able to run multiple apps over wsl
with each of them having their unique domain names. So here's what my hosts
file on windows looks like:
127.65.43.21 localwslnginx.com
127.65.43.22 localwslapache.com
# Laravel Apps
127.65.43.22 evangrest.test
127.65.43.22 firstbarcodes.test
They must bear the same static IP with the localwslapache.com
or localwslnginx.com
for apache
and nginx
respectively.
For apache
, I used the following config to get my laravel
app running:
<VirtualHost *:80>
DocumentRoot "/data/www/firstbarcodes/public"
DirectoryIndex "index.php"
ServerName firstbarcodes.test
ServerAlias firstbarcodes.test
<Directory "/data/www/firstbarcodes">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<Directory "/data/www/firstbarcodes/public">
Options Indexes FollowSymLinks MultiViews ExecCGI
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>
#Alias /js /data/www/firstbarcodes/public/js
#<Directory /data/www/firstbarcodes/public/js>
# Options Indexes FollowSymLinks MultiViews ExecCGI
# AllowOverride All
# Order allow,deny
# Allow from all
# Require all granted
#</Directory>
</VirtualHost>
You might also need to run this command sudo a2enmod rewrite
for apache
I guess this way, I have been able to somehow create a hack for having a static IP for all my apache
and nginx
apps over WSL
.
What I would love to do next is to get my WSL
Debian
to auto-start my apache
and mariadb
on windows
startup.
I also had the same problem and came with my own solution. Here it is. It lacks adding static IPs with netsh
, however, has some other features :)
I have several WSL distros.
For each of them, I create a file named MYWSLDISTRONAME-hostnames.txt
in a scripts
directory.
This file contains only domain names:
app.ds.local
pma.ds.local
sandbox.ds.local
test.ds.local
At the beginning, I add these domain names to the end of the hosts
file, just once, like
127.0.0.1 app.ds.local
127.0.0.1 pma.ds.local
127.0.0.1 sandbox.ds.local
127.0.0.1 test.ds.local
If I have to add another domain name for my distro, I stop the WSL, and then just add
another.ds.local
line toMYWSLDISTRONAME-hostnames.txt
and127.0.0.1 another.ds.local
tohosts
Every time I need to start my WSL distro, I run the following script MYWSLDISTRONAME-start.ps1
. What it does:
- Starts the
/root/start_services.sh
script inside the distro that starts the necessary services etc (thus solving a problem with lack ofsystemd
) - Gets the IP address of running WSL distro
- Replaces IP with the new one in
hosts
for each domain name fromMYWSLDISTRONAME-hostnames.txt
. All this stuff is done with the temporaryhosts
file. - Checks if the real
hosts
file requires any modifications. If yes, a separatepwsh
is started that requests admin privileges. If not, the script just displays a note.
$runningDirectory = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$hostsFile = "c:\windows\system32\drivers\etc\hosts"
$wslHostnamesFile = "$runningDirectory\MYWSLDISTRONAME-hostnames.txt"
$tmpFile = "$runningDirectory\.new-hostnames.txt"
$distroName = "MYWSLDISTRONAME"
# Start services inside the WSL
wsl -d $distroName -e /root/start_services.sh
# Get IP address of WSL distro
$wslIpAddr = wsl -d $distroName -- ip addr
$match = [System.Text.RegularExpressions.Regex]::Match($wslIpAddr, "(?<ip>172\.[\d\.]*)\/")
$ip = $match.Groups["ip"]
Write-Host "$distroName is available at $ip"
# read hosts file and change the IPs
$host_file_contents = Get-Content $hostsFile -Encoding UTF8 -Raw
$hostnames = Get-Content $wslHostnamesFile -Encoding UTF8
foreach ($hostname in $hostnames) {
$host_file_contents = [System.Text.RegularExpressions.Regex]::Replace($host_file_contents, "(\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b)\s+$hostname", "$ip`t$hostname")
}
# Save hosts file
$host_file_contents | Set-Content -Path $tmpFile -Encoding UTF8
# Compare two files to avoid unncecessary popups
$result = Compare-Object -ReferenceObject ((Get-Content $hostsFile).trim() -ne '') -DifferenceObject ((Get-Content $tmpFile).trim() -ne '')
if ($result)
{
# Prepare and execute encoded command (note that we use 'pwsh' instead of 'powershell')
$command = "Get-Content -Path ""$tmpFile"" | Set-Content -Path ""$hostsFile"""
Write-Host "Updating hosts file with: $command"
$encodedCmd = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
Start-Process -FilePath pwsh.exe -Verb RunAs -ArgumentList "-encodedcommand $encodedCmd"
}
else
{
Write-Host "Hosts file is the same, modification is unnecessary"
}
Start-Sleep -Seconds 5
# Remove temporary file
Remove-Item $tmpFile
NOTE: I really like your idea with netsh
and probably will add it to my scripts.
And, for completeness, I also have MYWSLDISTRONAME-stop.ps1
:
wsl.exe --shutdown -d MYWSLDISTRONAME
I also have .lnk
's for both .ps1
files with nice and distinguishing icons to easily identify each distro :).
They can be started without admin privileges, the scripts will request them if necessary.