Creating a local HTTPS server for Create-React-App using nginx and certbot in WSL2 – for `npm start` AND `npm build`

If 2020 was a Scented Candle
How I feel after doing all this

Note: This article has some interesting WSL-centric material, like how to:

  • Set up bridge networking without Hyper-V
  • Find the UWP Ubuntu executable to run Linux commands outside of a WSL terminal
  • Create automatic startup tasks without systemd init system
  • Expose your (domain-enabled) dev environment to certbot/letsencrypt for automatic issuance of SSL certificates
  • Use nginx proxy for react

So even if you’re not doing the exact thing I set out to do (create an HTTPS dev & build preview server), you might still find these notes useful.

Update 6/11/2021: While the notes in this article basically work, I have determined that WuSLbun2 is not really suitable for doing backend development or deployment preparation. There are too many strange variables that have no parallel in a stock Ubuntu installation.

If you are looking for a platform to develop software you plan to deploy to an Ubuntu VPS, I recommend using a virtual machine. I have taken to doing front-end dev in WSL and copying anything that needs additional tooling/config to a “real” Ubuntu VM *first*, getting it ready on that, and *then* pushing it to VPS.

Interestingly, if you run Alpine on your web server, WSL might actually be more suitable for backend tooling configuration, as Alpine uses OpenRC rather than systemd as its init system – it’s probably more likely that you could use it in the WSL environment similarly to how you’d run it bare metal. I have not verified this first-hand, this guess is purely informed speculation from using Alpine extensively for various servers and containers – but never for WSL. Try it, find out, and message me!

Examples of major weird / unparalleled issues I ran into re: using WSL for backend tooling are (from orig post):

  • Windows controls all the networking. That makes setting stuff up that requires networking really strange. In addition, the bridge setup I enabled has needed to be re-adopted manually after every Windows reboot, the WSL vNIC won’t stay configured to stay in the bridge persistently.
  • There’s no actual init system. That means no systemd for enabling / starting / stopping software. /etc/init.d/$INITSCRIPT is present, but has no upstart control, so =>
  • All “init” jobs are handled by Windows Task scheduler – either that, or you just don’t start scripts automatically (unless there’s another method I’m not aware of)

I still like WSL2 for the additional flexibility it gives me in Windows, but it’s been “liberating” to recognize its deficiencies and put aside trying to get it to do everything I want – it’s just not there yet. The sooner I realized that, the sooner I stopped trying to wrap its alien configuration methods to purposes for which they are ill suited, and got back to coding.

However, if you want to try to get it to do stuff it’s not “supposed” to do, I left these notes up in the hope that someone else would come along and build on what I learned after several days of frustrating experiences.

Orig post:

This is a heavy post. This is the kind of thing I do when I’m working through a tough problem – well organized notes are a must. Obviously, parts of this will make more sense to me than you, but I have the added benefit (shortcoming?) of having trouble making sense of my notes when I come back to them, so I try to make them as complete as possible the first time so hopefully if it feels like I’m “reading them again for the first time” they still make sense.

This was an environment I set up over a night setting up certbot with nginx for testing a create-react-app project, which sounds trivial, but there are some definite caveats and pitfalls:

  1. I’m using it for a dev environment, which is the opposite of what certbot is intended for (but it does make some things easier, if you can get it to work in the first place…). You have to set it up with a sort of “this could be for production” mindset, as it requires a lot more bootstrapping than your average dev env.
  2. I’m using WSL2, which is just weird to configure coming from a bare-metal distro, and not well documented for what I’m attempting (I don’t think there are any other attempts documented on the net thus far – including disparate forum threads, github issues, reddit, etc.). I know Microsoft really tries, and there’s nothing else quite like WSL2, so they deserve some kudos for even creating this Linux compatibility layer, but… well… if you try it, you’ll see what I mean…
  3. The networking doesn’t ever really work right without manual intervention after each boot. It’s not that big of a deal, but it further belies how WSL is really lacking in proper integration with Windows. It works, but it’s hacky – it’s not ready for prime-time.

Certbot, nginx and CRA on Ubuntu 20.04 LTS WSL2 dev environment

There’s a really comprehensive guide about deploying CRA with nginx and certbot for HTTPS over here:

Can check against this article, but it does not cover WSL – so if you’re working w/ WSL it’s only good for some things.

some baseline info — env:

ISP: comcast residential cable (dynamic IP with namecheap dynamic DNS)
Home domain name:
Current home gateway IP: (fake)
LAN subnet: (real)
WSL target IP: (why not)
Windows: 21h1 (bloody)

Also have a 256GB NVMe with Ubuntu 21.04 on it I can run through VMWare Workstation, but surprisingly WSL2 is more performant (although the VMware VM has a GUI, so apples and oranges…). I like the WSL integration, but this hairy config stuff is a little tiresome. Oh well, I’m in this far now…


└─ ▶ cat /etc/os-release && uname -a
VERSION="20.04.2 LTS (Focal Fossa)"
PRETTY_NAME="Ubuntu 20.04.2 LTS"
Linux wharfrat 5.4.72-microsoft-standard-WSL2 #1 SMP Wed Oct 28 23:40:43 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

make sure focal-updates/universe repo enabled in /etc/apt/sources.list:

└─ ▶ grep -v "#" /etc/apt/sources.list
deb focal main restricted

deb focal-updates main restricted

deb focal universe 
deb focal-updates universe # <-- this is our repo here

deb focal multiverse
deb focal-updates multiverse

deb focal-backports main restricted universe multiverse

deb focal-security main restricted
deb focal-security universe
deb focal-security multiverse

Universe repo certbot package maintainer’s info:

└─ ▶ apt show certbot
Package: certbot
Version: 0.40.0-1ubuntu0.1
Priority: extra
Section: universe/web
Source: python-certbot
Origin: Ubuntu
Maintainer: Ubuntu Developers
Original-Maintainer: Debian Let's Encrypt
Installed-Size: 51.2 kB
Provides: letsencrypt
Depends: python3-certbot (= 0.40.0-1ubuntu0.1), python3:any
Suggests: python3-certbot-apache, python3-certbot-nginx, python-certbot-doc
Breaks: letsencrypt (<= 0.6.0)
Replaces: letsencrypt
Download-Size: 17.9 kB
APT-Sources: focal-updates/universe amd64 Packages

Description: automatically configure HTTPS using Let's Encrypt
The objective of Certbot, Let's Encrypt, and the ACME (Automated
Certificate Management Environment) protocol is to make it possible
to set up an HTTPS server and have it automatically obtain a
browser-trusted certificate, without any human intervention. This is
accomplished by running a certificate management agent on the web

This agent is used to:
Automatically prove to the Let's Encrypt CA that you control the website 

Obtain a browser-trusted certificate and set it up on your web server
Keep track of when your certificate is going to expire, and renew it

Help you revoke the certificate if that ever becomes necessary.

This package contains the main application, including the standalone
and the manual authenticators.

first, you’ll want to set up WSL2 so it can work in bridge mode. Create a bridge in Control Panel\Network and Internet\Network Connections by ctrl-clicking vEthernet (WSL) and Ethernet (or WIFI), right-clicking and selecting ‘create bridge

I took screenshots! Look at me go…:

Control Panel\Network and Internet\Network Connections
demonstration of making a network bridge in Windows
demonstration of making a network bridge in Windows

AFAIK you have to re-add WSL NIC in control panel every time you reboot (through the right-click -> properties menu) – trying to work around this, but haven’t found a solution yet as of 8am 6-5-2021

Then go to services.msc and disable Internet Connection Sharing (ics) – find it, right click, go to properties and select ‘disabled‘.

AFAIK this admin template is for domain networks only, so if in a domain env, try doing this:

Go to group policy editor and search for ‘ics‘ keyword, there will be only one administrative template. Enable it – it’ll disable internet connection sharing on your domain

The “enable disable of ics” administrative template, likely making my hs English teacher roll in her grave.
If Microsoft would have decided that all administrative templates enable a feature, they wouldn’t have to spend 10 paragraphs explaining whether the template turns something on or off…

TBH neither one of these methods really stopped ICS from being started with Ubuntu, but with the help of the ip commands (make a script!) it’s relatively painless to get back to a workable bridge network connection after reboot – just re-add WSL NIC in control panel to the network bridge, and re-run these ip commands (coming up…). I had varying success getting the ICS service to stop in services.msc – it doesn’t really seem to matter.

Next, open Ubuntu and run these commands – choose whatever IP and gateway you’d like/need:

sudo ip addr flush dev eth0
sudo ip addr add dev eth0
ip addr show dev eth0
sudo ip link set eth0 up
sudo ip route add default via dev eth0
ip route show

install ifupdown – and if you want, optional ifupdown-extra scripts:

sudo apt update && sudo apt install -y ifupdown [ifupdown-extra]

and add something like this to /etc/network/interfaces:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static

Add these two lines using visudo so you can start nginx without a password:

# Allow members of group sudo to execute any command

%sudo   ALL=(ALL:ALL) ALL
%sudo ALL=NOPASSWD: /etc/init.d/nginx start
%sudo ALL=NOPASSWD: /etc/init.d/networking start

Get the path of Ubuntu.exe so you can create a task in task scheduler using Ubuntu UWP install – note, you can find the exact path in Task Manager while Ubuntu window is open by finding wsl.exe or Ubuntu.exe, right-clicking and going to details, the right-clicking on Ubuntu.exe and showing file location. It’ll be something like this:

"C:\Program Files\WindowsApps\CanonicalGroupLimited.UbuntuonWindows_2004.2021.222.0_x64__79rhkp1fndgsc\ubuntu.exe"

Then you can use it to run the service from Windows and set it up in Task Scheduler to start on login, boot, etc.:

"C:\Program Files\WindowsApps\CanonicalGroupLimited.UbuntuonWindows_2004.2021.222.0_x64__79rhkp1fndgsc\ubuntu.exe" run sudo /etc/init.d/nginx start

try restarting to see if it works … (open an ubuntu window and see if you have networking – your /etc/network/interfaces IP, etc. – save those ip commands in a script, just in case!…)

install nginx and certbot w/ certbot nginx plugin and docs:

sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx python-certbot-nginx-doc

Get loopback and ethernet card info – omit loopback (no need to do for loop) if only assessing outward-facing network, or plan to use certbot instead of openssl self-signed cert:

└─ ▶ for i in eth0 lo; do ip addr show dev $i; done

# output:

4: eth0: mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:ab:6b:b6 brd ff:ff:ff:ff:ff:ff
    inet brd scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:feab:6bb6/64 scope link
       valid_lft forever preferred_lft forever
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

WSL: this address is only available on your local machine (because it’s a NAT address). You want an address on your LAN.

Start ngnix – native ubuntu:

└─ ▶ sudo systemctl enable --now ngnix

Start ngnix – WSL – the old upstart way, but without upstart (see Windows Task Manager workaround – yes, you read that right):

└─ ▶ sudo /etc/init.d/nginx start

# output:
Starting nginx nginx [ OK ]

check ip address (either lo or eth0):

Browser: http://localhost


Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.
. . .

set /etc/ngnix/sites-available/default settings:

sudo vim /etc/nginx/sites-available/default

set firewall for 80 and 443

( beyond scope – see firewall-cmd, ufw, iptables, ntables, bpf, etc. documentation )

copy virtual site portion of nginx default config

cd /etc/nginx/sites-available && cat default

# (selected and copied virtual section to file called

vim # name of dev site - I pasted default virt config here

sed -i 's/#//g' # get rid of all the comment leaders

vim # added a couple hashes for top comment, edited site name, added https port - example:

# File: /etc/nginx/sites-available/

cat /etc/nginx/sites-available/
# You can move that to a different file under sites-available/ and symlink that to sites-enabled/ to enable it.

server {
listen 80;
listen [::]:80 ipv6only=yes;


   root /var/www/lindabnotary; ### end new bits
   index index.html;

   location / {
           try_files $uri $uri/ =404;

The nice thing about certbot, is you can create a super easy cursory config file like this and when you invoke the script it’ll take care of the rest of the config for you in an optimized fashion. All it has to be able to do is connect with your endpoint via http – with the correct domain name in nginx. Which brings me to the next subject…

I have Windows AD DNS, so I created a DNS entry for my local dev server:

DNS server RSAT –> create A + PTR records: @,

If you don’t have a DNS server, set up dnsmasq or similar (out of scope) – one easy way off the top of my head is to create a record in pfsense/opnsense to the eth0 ip addr

The pfsense alias thing will work for local dev, but you won’t be able to create a certbot instance through its automated cert creator unless you have an externally-accessible domain name (ala one purchased through a registrar, like namecheap, godaddy, etc.). So if this is your case, either quit while you’re ahead and experiment on your VPS, get a DN through namecheap and link it up to your home’s external IP real quick, or read up on how to generate a cert using certbot manually (or using openssl to create a self-signed cert).

certbot manual instructions (note: I have no idea if this actually works the way I’m hoping it would):

I’m noticing there’s also dns-standalone plugin which is presumably for bind, etc. but probs still needs an external DN.

create symlink to dev build:

└─ ▶ cd /var/www && ln -s /home/avery/node/lindabnotary/build ./lindabnotary

└─ ▶ ls -la
total 12
drwxr-xr-x 3 root root 4096 Jun 4 22:16 .
drwxr-xr-x 14 root root 4096 Jun 4 20:26 ..
drwxr-xr-x 2 root root 4096 Jun 4 20:26 html
lrwxrwxrwx 1 root root 35 Jun 4 22:16 lindabnotary -> /home/avery/node/lindabnotary/build

create symlink between sites-available and sites-enabled:

└─ ▶ sudo ln -s /etc/nginx/sites-available/

(re)start nginx:

└─ ▶ sudo /etc/init.d/nginx start

# output: 
Starting nginx nginx

navigated to dev site in browser

Returned: “It works!” 😂😂😂

Then once the nginx server is serving the content locally, it’s time to set it up for access on the internet.

Necessity for web access – certbot requires internet access to your site in order to work. You must be able to get the dev server to respond to an http request on the internet. Therefore, you must open a firewall port to your dev server (at least temporarily) through external firewall/gateway, etc.

I did this in OPNsense. Here’s a thread/guide if you have questions:

(Through web GUI):
Location: Firewall –> NAT –> Port Forward

  • Firewall: NAT: Port Forward
  • Interface: WAN
  • TcpVersion: IPV4
  • Protocol: TCP
  • Destination: WAN address
  • Destination port range: HTTP, HTTPS
  • Redirect Target IP
  • Pool options: default
  • Description: WSL on Dell Laptop WharfRat (for dev purposes)
  • Set local tag: (optional)
  • Match local tag: (if you have one)
  • Nat reflection: Enable
  • Filter rule exception: (Will autocreate when saved)

Then, now that the external firewall rule is enabled to allow access from the internet, get your gateway’s current external ip address:

└─ ▶ curl -s | grep Hello | cut -d '>' -f2 | cut -d '<' -f1


And use the IP address to see if you can access your site from outside – There’s lots of ways you could do this, but I used my cell phone’s browser after turning off WIFI.

Real quick: If you mess your domain’s config up, you can do this to start over:

└─ ▶ sudo certbot delete --cert-name

OK, now for the moment of truth – run this command for certbot to automatically create a certificate and configuration:

└─ ▶ sudo certbot run --nginx -d

Another quick note: My namecheap dynamic DNS only allows one subdomain at a time, so I did not configure the DN. The subdomain ‘lindabnotary’ is already a wildcard – it’ll link to whatever server is presented through the firewall, not a specific server identified by the subdomain, like a “real” static DN.

But if you wanted to (or it would matter in your case, unlike mine) you could just string another ‘-d‘ on the end (presumably, you can do as many -d‘s as you want).

Here’s the output – I selected 2 for redirecting all traffic to HTTPS:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Obtaining a new certificate
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.

1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.

Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/

Congratulations! You have successfully enabled

You should test your configuration at:


Congratulations! Your certificate and chain have been saved at:
Your key file has been saved at:
Your cert will expire on 2021-09-03. To obtain a new or tweaked
version of this certificate in the future, simply run certbot again
with the "certonly" option. To non-interactively renew all of
your certificates, run "certbot renew"
If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt:
Donating to EFF:

Note: Now nginx config has been altered by certbot – here’s what it looks like after running the installation command:

You can move that to a different file under sites-available/ and symlink that to sites-enabled/ to enable it. Don’t forget to restart nginx.

   server {

       root /var/www/lindabnotary;
       index index.html;

       location / {
           try_files $uri $uri/ =404;

   listen [::]:443 ssl ipv6only=on; # managed by Certbot
   listen 443 ssl; # managed by Certbot
   ssl_certificate /etc/letsencrypt/live/; # managed by Certbot
   ssl_certificate_key /etc/letsencrypt/live/; # managed by Certbot
   include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
   ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

server {
    if ($host = {
        return 301 https://$host$request_uri;
    } # managed by Certbot
   listen 80;
   listen [::]:80 ipv6only=on;

return 404; # managed by Certbot

Troubleshooting: I noticed after I got the lock icon locally, I still couldn’t access my site remotely. For dev purposes, this might not be a big deal – I really just need to do development work locally, I’ll worry about external HTTPS access once I get it on the VPS.

Not having port 80 access and not being able to access 443 from the internet WILL prevent certbot from updating the certificate automatically, but I could just open up port 80 access again (instead of auto-redirect) in the /etc/nginx/sites-available/ file for automatic updates to work.

Hopefully this is just a small issue like a firewall port not being open, or something – will need to go re-check my OPNsense config…

Further dev stipulations: Now that I have my .pem file from certbot, I can link to it while running the development server, by editing the project folder’s package.json start script to utilize the ssl cert+key.

Make a dir to store the .pem files in locally, and copy them over because we need to modify the permissions

mkdir ssl
sudo cp /etc/letsencrypt/live/ ./ssl/fullchain.pem
sudo cp /etc/letsencrypt/live/ ./ssl/privkey.pem

These are wrong permissions for production, but must be able to execute as user other than root (prod perms: 0700 dir, 0600 each file, owned by root)

sudo chown -R root:root ./ssl
sudo chmod -R 755 ssl
sudo chmod 644 ssl/*.pem

# it’s possible you could change the owner and decrease the accessibility of the .pem files, have not tried. Play around with it and find out (chown -R user:user ./ssl, chmod -R 0700, chmod 0600 ./ssl/*.pem)

Ignore that, I tried this:

avery @ wharfrat ~/node/lindabnotary
└─ ▶ sudo chmod 0653 ./ssl/.pem avery @ wharfrat ~/node/lindabnotary └─ ▶ sudo chmod -R 0754 ssl avery @ wharfrat ~/node/lindabnotary └─ ▶ sudo chmod 0653 ./ssl/.pem
avery @ wharfrat ~/node/lindabnotary
└─ ▶ npm start

> start
> bash

EACCES: permission denied, open '/home/avery/node/lindabnotary/ssl/fullchain.pem'

Just make the ssl dir owned root, perms readable all users – it works, this is not production.

You can embed all these variables into package.json, but I opted to make a separate script

filename: ~/projectfolder/

#!/usr/bin/env bash

export HTTPS=true
export SSL_CRT_FILE=./ssl/fullchain.pem
export SSL_KEY_FILE=./ssl/privkey.pem 

react-scripts start

Make the script runnable:

chmod +x ./

and then modified package.json to run the script:

  "scripts": {
    "start": "bash", # this is our mod
    "build": "HTTPS=true react-scripts build",  # not sure if HTTPS=true does anything during build - look into
    "test": "react-scripts test", # FYI you cannot put comments in json files
    "eject": "react-scripts eject"

An aside: This bash script is not runnable outside of npm start (cannot just run ./ – needs to be invoked by npm to run react-scripts).

Easiest WSL2 Bridge Network (without Hyper-V Virtual Network Manager)

Burning a Wireless Router - YouTube
Grilling a Linksys. Delicious…

I am testing certbot locally with Create-React-App, but I’m using WSL2. Little did I know, the networking for Ubuntu in WSL2 is a little strange when coming from your typical systemd-network, NetworkManager, ifupdown, <insert your favorite networking API>

Apparently, the networking is taken care of in the lxssManager service (you can reveal it / restart it, etc. in task manager – ctrl-shift-esc -> services). This is because WSL doesn’t have an init system, unless you get some kind of hack to give you a systemd-ish responsive system – I’ve heard of something called ‘genie’, but I haven’t wanted to try it, since I’m trying to do WSL the “right” way 😂

Anywho, if you want an IP from your WSL installation that’s in the same subnet as your LAN, you’re out of luck, since the IP address supplied to your WSL instance is provided through NAT. Some threads I was scanning while looking for a solution talked about installing Hyper-V Virtual Network Manager, but wanting to keep things as light as possible, I have no interest in installing Hyper-V or any of its tools just to get a bridge network setup for WSL.

So I poked around the Network Connections in the Control Panel – this can be revealed by searching for Control in the start menu (it’s been getting progressively more hidden every release since the introduction of the UWP “Settings” app). Once you’re in there, go to Control Panel\Network and Internet\Network Connections (you can paste that line straight in the address bar) and look at what you’ve got. You should see something like this:

Control Panel Network Connections – quaint, isn’t it?

Although it’s likely you won’t have a “Network Bridge”… yet.

Here’s what to do:

Highlight your Ethernet (or WIFI) connection (or both) and the “vEthernet (WSL)” adapter (can select multiple by holding down CTRL while clicking on them), then right-click and select “Create Bridge” from the menu.

That’ll take a minute, but should leave you with this thing:

network bridge software adapter (Windows 10 control panel)

One of the threads I read said you have to restart after creating this thing, but that was not my experience. If you’re having weird networking issues you can’t figure out, you might want to give that a shot.

Then once you have it bridging your WSL adapter with your choice of wired-NIC or WIFI, head over to your bash prompt and set the adapter – note, my LAN’s subnet is, you’ll want to adjust that for whatever yours is:

 sudo ip addr flush dev eth0
 sudo ip addr add dev eth0
 ip addr show dev eth0
 sudo ip link set eth0 up
 sudo ip route add default via dev eth0
 ip route show

So I’ll run through these real quick:

First you flush your old NAT ip settings, then you assign an address to your WSL adapter. I just picked one at random, but I’d definitely recommend checking to make sure you don’t have the IP address assigned first (try an IP scanner, like arp-scan, or something in Windows – note: I always get horribly paltry results with arp -a for some reason).

Give your WSL eth0 adapter the IP in CIDR notation, then set it to up so you can create a route for it. Create the route to your local gateway (e.g. or whatever yours is…)

Show that your address has been created, the default route has been created, then I like to ping a local machine like my gateway, then something that requires name resolution (here I used google).

Funny thing is, that actually worked before setting up the DNS resolver file, but you have to do that next. I don’t think it was pinging using ipv6, so that doesn’t explain why it worked – probably some weird Windows networking dichotomy, but sure enough, nothing else will really work in name resolution land beyond that, so it’s time to do the next step…

You’ll want to move the /etc/resolv.conf file to /etc/wsl.conf (unless you already have a wsl.conf file, of course – but it’s not created by default). I just invoked # mv /etc/resolv.conf /etc/wsl.conf since it already had the lines I had to add to wsl.conf in it, and I was about to replace it anyway…

Make your /etc/wsl.conf file look like this:

└─ ▶ cat /etc/wsl.conf
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
generateResolvConf = false

Then create an /etc/resolv.conf file using your favorite DNS servers (,,, etc.) I have local DNS, so here’s mine:

# echo 'nameserver
> nameserver
> search' > /etc/resolv.conf

Run something like # apt update to make sure stuff’s resolving now. In my case it was all good.

Note: These ip commands are ephemeral, so you’ll probably want to do some research into how to making them more permenant. I literally put that first block of ip commands in a script to run for when I come back after a restart, because I’m almost 100% certain all this setup will be lost.

One really interesting caveat is that I noticed the Network Bridge in the Control Panel didn’t include the “vEthernet WSL” adapter when I restarted. The bridge was still there, but only had my Ethernet adapter in it, so I had to manually add the WSL adapter again manually in properties.

There’s apparently two config files – one’s the /etc/wsl.conf file we just created, the other is C:\Users\YourUserName\.wslconfig

Unfortunately, the /etc/wsl.conf file is pretty limited as to network config – I did add the generateHosts = false line in mine, because my /etc/hosts had a bunch of weird Windows-only subdomains created by programs that are totally unnecessary to have in Linux, but there’s a lot of other neat stuff in there related to selective case sensitivity, mounting different filesystems, etc. you might want to check out.

So I’m still searching for ways to make this permanent, but once I figure it out, I’ll come back and finish this post. Might have to send an issue to the devs over on GitHub, but I’ll get this working eventually. For now, adding WSL back in the bridge if I restart and running my script isn’t that big of a deal.

Update: This post is a small part of a comprehensive project I was working on. So far, I have posted my raw notes for others to scour if they want to try and re-create the environment:

Fetch a file from a Clonezilla image without restoring to disk

Dinosaurs are cute

I thought this was neat, but I got the info from a site that presented it in a way that was a bit difficult to decipher, so I wanted to pass it on in a presentation that is easier to understand.

If you’re not using CloneZilla, you’re definitely missing out. It’s a super easy way to clone hard drives, USB thumb drives, etc. into a format that’s easy to restore. It does disk-to-image, disk-to-disk, disk-to-network supporting ssh, nfs and cifs, and even has a p2p cloning framework for two networked computers using CloneZilla live USBs (!)

Needless to say, that’s definitely cool. But what if you want to grab a file out of an archive you’ve already created?

I had made a backup of a client’s computer I wanted to have in case something went south, but I needed to make sure I could get to the files in case I was asked to deliver files while the computer was in a state of (dis)repair.

Well, thankfully that’s pretty easy, since CloneZilla’s default image format is just some gzipped archives. You basically just unzip them into another file, which appears as the raw image file (e.g. .img), which at that point you can mount and traverse like any other mounted drive.

The basic structure of an image is contained inside a directory created by CloneZilla. It looks like this:

$ ls -la
total 55474810
drwxr-xr-x 2 avery avery                  43 May  5 18:57 .
drwxrwxrwx 9 avery administrators         16 May  5 21:36 ..
-rw-r--r-- 1 avery avery                 874 May  5 17:28 blkdev.list
-rw-r--r-- 1 avery avery                 706 May  5 17:28 blkid.list
-rw-r--r-- 1 avery avery               10068 May  5 18:57 clonezilla-img
-rw-r--r-- 1 avery avery                 235 May  5 18:56 dev-fs.list
-rw-r--r-- 1 avery avery                   4 May  5 18:57 disk
-rw-r--r-- 1 avery avery                 482 May  5 18:57 efi-nvram.dat
-rw-r--r-- 1 avery avery                7354 May  5 18:57 Info-dmi.txt
-rw-r--r-- 1 avery avery                 236 May  5 18:57 Info-img-id.txt
-rw-r--r-- 1 avery avery               19252 May  5 18:57 Info-lshw.txt
-rw-r--r-- 1 avery avery                2401 May  5 18:57 Info-lspci.txt
-rw-r--r-- 1 avery avery                 313 May  5 18:57 Info-OS-prober.txt
-rw-r--r-- 1 avery avery                 211 May  5 18:57 Info-packages.txt
-rw-r--r-- 1 avery avery                 114 May  5 18:57 Info-saved-by-cmd.txt
-rw-r--r-- 1 avery avery               10192 May  5 18:57 Info-smart.txt
-rw-r--r-- 1 avery avery                  20 May  5 18:57 parts
-rw------- 1 avery avery            13784650 May  5 17:28 sda1.vfat-ptcl-img.gz.aa
-rw------- 1 avery avery             7477849 May  5 17:28 sda2.dd-ptcl-img.gz.aa
-rw------- 1 avery avery          4096000000 May  5 17:36 sda3.ntfs-ptcl-img.gz.aa
-rw------- 1 avery avery          4096000000 May  5 17:42 sda3.ntfs-ptcl-img.gz.ab
-rw------- 1 avery avery          4096000000 May  5 17:48
-rw------- 1 avery avery          4096000000 May  5 17:54
-rw------- 1 avery avery          4096000000 May  5 18:00
-rw------- 1 avery avery          4096000000 May  5 18:07
-rw------- 1 avery avery          4096000000 May  5 18:13
-rw------- 1 avery avery          4096000000 May  5 18:19 sda3.ntfs-ptcl-img.gz.ah
-rw------- 1 avery avery          4096000000 May  5 18:25
-rw------- 1 avery avery          4096000000 May  5 18:32 sda3.ntfs-ptcl-img.gz.aj
-rw-r--r-- 1 avery avery               16384 May  5 17:28 sda-gpt-2nd
-rw-r--r-- 1 avery avery               17920 May  5 17:28 sda-gpt.gdisk
-rw-r--r-- 1 avery avery                 790 May  5 17:28 sda-gpt.sgdisk
-rw-r--r-- 1 avery avery                 512 May  5 17:28 sda-mbr
-rw-r--r-- 1 avery avery                 621 May  5 17:28 sda-pt.parted
-rw-r--r-- 1 avery avery                 551 May  5 17:28 sda-pt.parted.compact
-rw-r--r-- 1 avery avery                 849 May  5 17:28 sda-pt.sf

They’re logically named with tags for partitions, like sda1, sda2, etc. You can see the super huge files are .gz.* files – gzip archives (or zstd, depending on what option you chose) that have to be re-combined to decompress properly, but that’s no big deal.

If you want to save a partition to an image file, make sure you have a copy of partclone installed on your system (e.g. # apt install partclone -y if on a debian-based distro). That’ll automate the restructuring of the partition from the gzip archives.

Make sure you have enough room on whatever drive you’re doing it on for the entire partition (and then some) – might want to du and df first just to be safe.

Note: if you used zstd instead of gzip for the archives, you’ll have to look up the zstd command switches and adapt the one-liner to accommodate the differences in their APIs. I haven’t tried it, but I assume the result should be the same.

Here’s a quick run-down of how the process goes:

# These instructions assume you're in a dir containing the backup dir
$ ls -la
drwxrwxrwx  9 avery                       administrators           16 May  5 21:36  .
drwxr-xr-x  6 root                        root                      7 Mar 23 00:21  ..
-rw-------  1 root                        root               14196736 
drwxr-xr-x  2 avery                       avery                    43 May  5 18:57  laptop-drive-backup 

# create the file that will be your raw image - any name will do
$ touch laptopPartition.img

# pipe the archives to gzip and use partclone to construct the .img 
$ cat ./laptop-drive-backup/sda3.ntfs-ptcl-img.gz.* | gzip -d -c | partclone.restore -C -s - -O ./laptopPartition.img

# then you can mount your .img file as a loop
$ sudo mount -o loop -t ntfs ./laptopPartition.img /mnt/someEmptyDir

# and browse it like anywhere else
$ ls -la /mnt/someEmptyDir # which is now loop-mounted .img file

Another thing to note is that loop mounts are read-only, kind of like mounting an .iso file, or more recently, a snap container. So you can copy stuff off, but you can’t put stuff on. But this is just a copy of your original .gz.* backup, so there’s really no chance in harming the original’s integrity anyway.

It’s always important to conduct yourself with the utmost (file) integrity!

Compiling and installing GitHub’s Hub Utility in msys2

Hub project source code root directory

So I’ve been liking msys2 so far. I’ve replaced the default git install with msys2’s utilities in C:\tools\msys64\usr\bin and they seem to work from Windows command prompt just fine, except for bash, which complains of cygwin1.dll mismatch, but that’s OK, I’ll just run the msys2_shell.cmd which is how it’s intended to be used anyway (inside Mintty – or, alternately ConEmu or defterm). So it’s slightly less flexible (git-bash’s bash.exe could be run from inside a standard cmd.exe terminal), but I’m OK with that since I now have man files in Windows (!) and I can update the utilities using pacman, instead of waiting for updates to come through git-bash updates (I have my doubts that it ever happens).

Why do all of this hacky 3rd-party linux sublayer stuff instead of just enabling Windows Subsystem for Linux (WSL)? Simple: I am fairly certain you cannot run WSL binaries from Windows (please correct me if I’m wrong!).

Continue reading “Compiling and installing GitHub’s Hub Utility in msys2”

man command in Windows possible (?)

Continuing along in my series about how to make Windows more palatable for Linux users, one thing that’s been more difficult to find a workaround for is the man command

For about an hour, I went on a wild goose chase for some sort of alternative in Windows, and found relatively little. I discovered a win32 implementation of mandoc here, which sounds interesting, but I decided not to explore it further than seeing if it’d run (spoiler: it does), since where would I download all the man files for the utilities that were included with git-bash? Sounded like too much of a chore.

Instead, I opted to go with an entirely new shell framework, called msys2. It’s slightly different than git-bash, in that, although both link to cygwin.dll for their tools, it starts logins an entirely new user folder in the C:\tools\msys64\home\user directory, and uses pacman as a package manager. You can supposedly compile tools that are totally win32 compatible with minGW (also included in the installation package) without having to link to cygwin.dll, but I’ve yet to see that in action.

Besides, all I really cared about at the time was getting my man back! Does that sound like you? Are you also a Windows user missing your man? Well, read further!

Continue reading “man command in Windows possible (?)”

More ways to help Linux users hate Windows less

Evil Computer Laptop - Openclipart

Ok, so obviously if you hate Windows, you probably just shouldn’t be using it. But what if you love the way Linux operates, but have a plethora of Windows-only applications you have to have at your fingertips at any moment’s notice?

Here are some ways the discerning Linux user who’s stuck by vendor lock-in can make Windows a little more palatable in the command line:

Continue reading “More ways to help Linux users hate Windows less”

The GRUB prompt: Demystified

Free Computer People Cliparts, Download Free Computer People Cliparts png  images, Free ClipArts on Clipart Library

OK, so practically everyone who uses Linux has come to this menacing prompt at least once or twice. Typically, unless you’re a seasoned sysadmin, it’s a harbinger of death, requiring more than you bargained for to be able to use the computer that day without dire intervention.

But if you know how to navigate the GRUB prompt, you can get back to work just like you were hoping.

These two skills are extremely useful to have in your arsenal of command-line fu, because if you can knowledgably tackle a GRUB prompt in a reasonable amount of time, it can save you hours of headaches and hassle.

So don’t be afraid, embrace the unknown and learn how to navigate GRUB!

Continue reading “The GRUB prompt: Demystified”

Easing pain of migration from Google Hangouts to Google Voice (as much as humanly possible)

Parting Hangouts is not sweet sorrow…

Some of us have been using Hangouts for ages. Essentially since Google recommended we switch from texting with Google Voice way back May of 2013.

Well, now they’ve dropped the hammer again, and said we have to switch back to Google Voice, since for whatever reason they’ve decided to terminate Hangouts altogether.

What gives? I’d been enjoying Hangouts quite a bit, as it fits my workflow perfectly, and had adapted to it in every minute detail through years of experience in daily life.

And now you want to make that all go away?

Continue reading “Easing pain of migration from Google Hangouts to Google Voice (as much as humanly possible)”

PSA: Menus not staying open in Supermicro IPMI? Here’s how to fix it:

I’ve noticed this a couple times in the last week – I had an iKVM window open on my Supermicro host, trying to control the ESXi DCUI, and the menu wouldn’t stay open. It’s very frustrating.

I don’t have a physical monitor hooked up to any of my hosts, so this is a pretty important thing to have working in the event I need to change a setting only available on the “physical” host’s menu.

So, in case you landed on this page because you’re trying to figure out how to fix the same issue, here’s what I discovered:

Continue reading “PSA: Menus not staying open in Supermicro IPMI? Here’s how to fix it:”

Automate kernel module installations for VMware Workstation on your Linux distro

VMWare Archives - The CloudStack Company
VMware Robot does a little dance

Update April 2021: I noticed VMware Workstation 16 in Ubuntu tends to install kernel modules fine through its GUI now, so a lot of this info about how to compile and install vmmon and vmnet is probably obsolete. But since it may be useful to someone using an older version of Workstation, I’ll leave it up for the time being.

In reference to this post I made earlier:

I found the most helpful script, just drop this in a text file called /etc/kernel/install.d/vmmodules.install

Continue reading “Automate kernel module installations for VMware Workstation on your Linux distro”