If you run gh auth setup-git, the GitHub CLI becomes git's credential helper — and it only ever hands out the token for the active account. The moment you git pull a repo belonging to your other account, you get the wrong token (403, or commits land under the wrong identity). There's no per-repo routing; you'd have to gh auth switch every single time you cross accounts.
You can prove it to yourself — ask the helper for the non-active account and it returns nothing:
"protocol=https`nhost=github.com`nusername=second-account`n`n" |
& gh auth git-credential get # exit 1, no token
The fix is to hand git over to Git Credential Manager (it already ships with Git for Windows). GCM picks the token by the username embedded in the remote URL, so routing becomes automatic. gh keeps working for gh issue/gh pr — it uses its own keyring, separate from the git helper.
Switch the helper and point GCM at GitHub:
git config --global --unset-all credential.https://github.com.helper
git config --global credential.helper manager
git config --global credential.https://github.com.provider github
git config --global credential.guiPrompt false # terminal prompts, no GUI popup on a server
…more
You protect ~/.bashrc.secret with chmod 444 so nothing accidentally overwrites your API keys and passwords. Then your token-refresh script needs to write a new session cookie somewhere — and hits Permission denied.
The fix isn't adding a second writable secrets file. It's stop persisting short-lived tokens entirely.
Refactor the refresh script to output TOKEN HASH on stdout and let the caller capture it:
# refresh_token — reads credentials, prints session token to stdout
CREDENTIALS=$(bash ~/bin/refresh_token 2>/dev/null)
TOKEN=$(echo "$CREDENTIALS" | awk '{print $1}')
HASH=$(echo "$CREDENTIALS" | awk '{print $2}')
No file writes. No second secrets layer. The token lives in the process environment for the duration of the operation and disappears when it's done.
This works because session tokens are cheap to re-obtain — one HTTP call with stored credentials. The only reason to persist them is avoiding that call, which is almost never worth the complexity of managing a writable token store alongside a read-only credential store.
The rule: if you can regenerate it from long-lived credentials in under a second, don't persist it.
If curl throws SSL certificate problem: unable to get local issuer certificate every time in WSL, it's usually a stale CA bundle — especially common on corporate networks running SSL inspection (Zscaler, etc.).
First, refresh the bundle:
sudo apt-get update && sudo apt-get install -y ca-certificates
sudo update-ca-certificates
That fixes most cases. If not, try a full reinstall:
sudo apt-get install --reinstall ca-certificates
sudo update-ca-certificates --fresh
On corporate networks, you likely need your company's root CA certs. You probably already have them somewhere in your Windows filesystem (.crt or .cer files). Copy them all into the trusted store:
sudo cp /c/Certificates/*.crt /usr/local/share/ca-certificates/
sudo cp /c/Certificates/*.cer /usr/local/share/ca-certificates/
sudo update-ca-certificates
Skip .pfx files — those contain private keys and are a different format.
If you don't have the certs handy, export them from Windows:
# PowerShell — list all root certs, look for your company name
Get-ChildItem Cert:\LocalMachine\Root | Select-Object Subject, Issuer | Sort-Object Subject
Or use certmgr.msc (Win+R → certmgr.msc) → Trusted Root Certification Authorities → find your company's cert → right-click → Export → Base-64 encoded X.509.
Test with curl https://google.com after each step.
Most EC2 Windows Server instances don't expose nested virtualization to the guest OS. WSL2 requires Hyper-V extensions, so it simply won't start. WSL1 works fine — it's a translation layer, not a VM.
Enable the WSL feature and reboot in an Admin powershell window:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
Restart-Computer
After reboot, pin the default version to 1:
wsl --set-default-version 1
On Windows Server, Add-AppxPackage is often broken or unavailable, so the standard wsl --install route fails. Use wsl --import with a rootfs tarball instead:
Now use a non-admin powershell:
mkdir $HOME\WSL
mkdir $HOME\WSL\Ubuntu
Then:
wsl --import Ubuntu $HOME\WSL\Ubuntu .\ubuntu.tar.gz --version 1
What you lose with WSL1: no Docker, no real Linux kernel, no systemd, weaker filesystem compatibility. Fine for CLI tooling, scripting, and build environments. If you need a real kernel, spin up a native Linux EC2 instance instead.
AWS console offers three ways to duplicate an EC2 instance, differing in whether disk data is carried over.
Create AMI (recommended, full clone) — preserves the system disk, installed software, and all configuration.
In the EC2 console, select the target instance → Actions → Image and templates → Create image. Wait for the AMI status to become available (a few minutes to tens of minutes), then go to AMIs → select it → Launch instance from AMI. Adjust instance type, subnet, Security Group as needed.
Launch More Like This (fastest, no data) — copies only instance configuration (type, SG, subnet, tags). The system disk is brand new.
Actions → Image and templates → Launch more like this. The Launch page opens with config pre-filled; confirm and launch. Good for stateless instances, e.g. web servers initialized via userdata.
Launch Template — if the original instance had a Launch Template saved, launch directly from it. EC2 → Launch Templates → select template → Actions → Launch instance from template.
Use AMI for most cases. Use Launch More Like This when you only need the same specs with a clean disk.