Introduction

Hello I'm Jimmy. This is a collection of notes and configurations that I've accumulated while setting up my development environment for day-to-day productivity.

I work with both Windows and macOS, and I rely heavily on Linux environments within containers or VMs. This site serves as a central repository for the things I learn while configuring these environments.

I'm making this information public in the hope that others might find it useful. Feel free to browse around, and I hope you find something that helps you in your own setup!

Windows

Windows has evolved into a powerful desktop environment, especially with the introduction of WSL2.

Essential Windows Productivity Tools

  • Powertoys: Utilities like FancyZones for window management, and Command Palette for quick app launching streamline my workflow.
  • Windows Terminal: A modern, customizable terminal that integrates seamlessly with WSL and support multiple profiles, themes, and keyboard shortcuts.
  • Hurl: Hurl is a utility that lets you choose which browser (like Firefox or Edge) to open when you click a link outside your browser.

Useful Resources

WSL2

Installation

Install wsl with the following command:

wsl.exe --install

Install FedoraLinux-42 distribution:

wsl.exe --install FedoraLinux-42

Keep your WSL installation updated by running:

PS E:\clones\homelabguide> wsl.exe --update
Checking for updates.
The most recent version of Windows Subsystem for Linux is already installed.

Configuration

Resource Allocation

Create or modify .wslconfig file to customize WSL2 resource allocation:

cd ~
notepad.exe .wslconfig

Here is a sample configuration:

# Settings apply across all Linux distros running on WSL 2
[wsl2]
memory=4GB
processors=4
swap=4GB
networkingMode=mirrored
dnsTunneling=true
autoProxy=true

[experimental]
autoMemoryReclaim=gradual

I'm not going into details why I configure with these settings, but you can read more about it here.

After making changes to .wslconfig, apply them by shutting down WSL:

wsl.exe --shutdown

Disable windows interop

I experience slowness when typing in the WSL terminal while the interop appendWindowsPath is enabled. You can disable it by configuring /etc/wsl.conf file:

[interop]
appendWindowsPath=false

Read more about it here

Accessing Your WSL Environment

Enter your Fedora Linux environment:

wsl.exe -d FedoraLinux-42

SSH Setup

Generate an SSH key for secure authentication:

ssh-keygen

To share your existing Windows SSH keys with WSL:

wsl -d FedoraLinux-42
cp -rv /mnt/c/Users/{replace_with_your_username}/.ssh/ ~
chmod 600 ~/.ssh/id_ed25519

Make sure to replace {replace_with_your_username} with your actual Windows username.

Key Principles

  • Always think Window and WSL as two separate operating systems.
  • Windows files are mounted inside WSL under /mnt/c, but this is actually a mounted Windows file system, not native Linux storage.
  • To avoid perfomance issues do not work with Windows files from Linux and avoid editing WSL files directly from Windows. If you need to sync files between the two environments, consider using a remote git repository or a dedicated syncroization tool like Mutagen.io, as these methods are more reliable for cross-platform workflows.

Useful Resources

Winget

To install winget, checkout this guide: https://learn.microsoft.com/en-us/windows/package-manager/winget/

Using Windows 10/11 IoT Enterprise LTSC user, without Microsoft Store, Follow this guide instead: https://learn.microsoft.com/en-us/windows/iot/iot-enterprise/deployment/install-winget-windows-iot

Installing apps with winget, First search for what you want:

PS C:\Users\Jimbo> winget search espanso
Name    Id              Version Source
---------------------------------------
Espanso Espanso.Espanso 2.2.3   winget

Then install it:

winget install --id Espanso.Espanso

Powertoys

Powertoys utilities that I find useful:

  1. Command Pallete
  2. FancyZones
  3. ZoomIt

Command Pallete lets you run apps by typing their name - similar to Spotlight on macOS.

FancyZones is help to create window layout. Press Win + Shift + ` to open the FancyZones editor.

I use 4 virtual desktops for different tasks:

  1. Terminal
  2. Code
  3. Browser
  4. Other

Switch between the virtual desktop using Ctrl + Win + Arrow left/right.

To jump to any open window from any desktop, use Command Pallete by typing < before the app name.

This setup is heavily inspired by DHH in the Omakub demo.

Windows Terminal

Configuration

configure, Catppuccin theme please follow the instruction in catppuccin

Change bell notification style to Flash taskbar, I find audible bell notification is annoying. Go to Settings > Defaults > Advanced > Bell notification style > and check Flash taskbar.

Add a new profile for a remote machine

Go to Settings > Open JSON file

in your editor of choice, add a new profile by adding this json object under the profiles.list array

{
    "commandline": "ssh jimbo@devbox",
    "name": "jimbo@devbox",
}

Install Nerdfont

Must install Nerd Fonts for the icons to work.

macOS

For macOS, I often work on a company-issued laptop but still need a Linux-like environment for development. Here's my streamlined setup:

  • Lima-vm: My go-to for running Linux virtual machines on macOS. It's lightweight and supports x86_64 containers with the --rosetta flag, making it easy to run and manage Linux containers locally.
  • Wezterm: I use Wezterm because it reads your SSH config directly, allowing you to open a new tab and SSH into a remote machine instantly - no extra setup required. This makes managing multiple remote sessions fast and seamless.
  • Amethyst: A tiling window manager that helps keep my workspace organized and simple keyboard shortcusts.
  • Maccy: A clipboard manager that boosts productivity.

Lima-vm

Installation

Follow installation instructions for Macos.

Create a new VM with the following command:

limactl start template://docker --rosetta

--rosetta flag is required for working with x86_64 containers, if you don't need it, you can omit it.

Accessing the VM with the following command:

limactl shell docker

Tunnel Access

If you want to browse using a browser that's aware of the network in the lima-vm instance, you can use the following command:

limactl tunnel --socks-port 1080 default

Later in the browser (I'm using firefox), you can set the SOCKS proxy to socks://localhost:1080.

Wezterm

Grab the latest release from here

Here is my lua config file:

local wezterm = require("wezterm")

local config = wezterm.config_builder()

config.color_scheme = "catppuccin-latte"

config.font = wezterm.font("Jetbrains Mono")

config.keys = {
	{
		key = "Enter",
		mods = "ALT",
		action = wezterm.action.DisableDefaultAssignment,
	},
}

-- https://github.com/wez/wezterm/discussions/4728
local is_darwin <const> = wezterm.target_triple:find("darwin") ~= nil

if is_darwin then
	config.font_size = 16.0
else
	config.font_size = 14.0
end

return config

Amethyst

I keep all windows fullscreen using the Fullscreen layout and navigate between them with Ctrl + Cmd + Arrow right/left.

I only use these 2 layouts:

  1. Fullscreen
  2. Two Panes

Layouts

Fullscreen is my go-to most of the time.

Shortcuts

Some apps like system settings work better floating, so I add them to the float list.

The apps that I keep it the float list:

  1. Telegram
  2. Appstore
  3. Finder
  4. Maccy
  5. Logseg

This setup is heavily inspired by DHH in the Omakub demo.

Homelab

This section covers tools available in Linux, especially related to containers, Kubernetes, and self-hosted apps.

Podman

Installation

Install podman

sudo dnf install -y podman

Compatibility with Docker

Install podman-docker

sudo dnf install -y podman-docker

Install single binary docker-compose

curl -SL https://github.com/docker/compose/releases/download/v2.36.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose

Make it executable

sudo chmod +x /usr/local/bin/docker-compose

Enable podman socket

systemctl --user --now enable podman.socket

Find your podman socket address

podman info | rg remote -A2

Set DOCKER_HOST environment variable

export DOCKER_HOST=unix:///run/user/1000/podman/podman.sock

You'll need to set the DOCKER_HOST variable each time you open a new terminal, or just add it to your .bashrc file.

Notes on Docker Compose with WSL

If you're running docker-compose and find an error like netavark (exit code 1): nftables error: "nft" did not return successfully while applying ruleset it' likely because limitation in how nftables operates in WSL.

To fix this, switch podman to use iptables as firewall driver.

Install iptables

sudo dnf install -y iptables

Open or create /etc/containers/containers.conf, and add the following:

[network]
firewall_driver="iptables"

Running Podman in the Background

For containers you want to keep running, podman has systemd integration called Quadlet.

Example for a browserless container:

Create a systemd service file in ~/.config/containers/systemd/browserless.container

[Unit]
Description=Browserless

[Container]
Image=ghcr.io/browserless/chrome:v2.27.0
PublishPort=3000:3000
AutoUpdate=registry
PodmanArgs=--memory=1g --cpus=0.8

[Service]
Restart=always

[Install]
WantedBy=default.target

Reload systemd

systemctl --user daemon-reload

Start and enable the service

systemctl --user start browserless.service
systemctl --user enable browserless.service

Check status

systemctl --user status browserless.service

We have to enable the linger for our user to start the containers without the user being logged in:

loginctl enable-linger $USER

Podman Auto-Update

The AutoUpdate=registry option helps update images automatically, but you still need to run:

podman auto-update

You can set up a cronjob to run this command regularly.

Useful Resources

Multipass: Ubuntu VMs Made Simple

Overview

Multipass is a streamlined solution for creating and managing Ubuntu VMs locally. It offers a "just works" experience that's perfect for:

  • Isolating different project environments
  • Testing with different dependency versions (PHP, Node.js, Go, etc.)
  • DevOps learning and experimentation
  • Container operations with Podman
  • Ansible automation practice

Installation

sudo snap install multipass

Verify installation:

multipass version

Basic VM Operations

Create VM with Custom Resources

multipass launch --name dev-vm -c2 -m4G -d20G "24.10"

-c2: 2 CPU cores -m4G: 4GB RAM -d20G: 20GB disk "24.10": Ubuntu 24.10 (includes latest Podman with Quadlet support)

Create VM with SSH Key

  1. Create cloud-init.yaml:
ssh_authorized_keys:
  - YOUR_PUBLIC_KEY_HERE
  1. Launch with cloud-init:
multipass launch --name dev-vm -c2 -m4G -d20G --cloud-init cloud-init.yaml "24.10"

Connect to VM

Get VM IP address:

multipass list

SSH directly:

ssh ubuntu@VM_IP_ADDRESS

Or use built-in shell:

multipass shell vm1

File Sharing

Mount local directory to VM:

multipass mount /path/to/local/directory vm1:/path/in/vm

VM Management

  • List VMs: multipass list
  • Start VM: multipass start vm1
  • Stop VM: multipass stop vm1
  • Delete VM: multipass delete vm1
  • Permanently remove: multipass purge

Ansible: Automation for Your Lab

Ansible is my go-to tool for automating home lab setup and management. For a clean install, I recommend using uv to manage Python and Ansible dependencies—it keeps your environment tidy and repeatable.

To test your Ansible playbooks, use Multipass to spin up local VM. Set up SSH access for the root user using SSH keys only, and always disable password login for security.

If you want a lightweight web UI to manage and schedule your playbooks, try Semaphore. It’s easy to set up, supports cron-like scheduling, and uses a portable Bolt database.

Managing Python Environments with uv

uv is a modern Python package and environment manager that stands out for several reasons.

  • Speed: uv is significantly faster than traditional tools like pip and virtualenv.
  • Portability: uv is a single binary with no dependencies, making it easy to use across different systems.
  • Container/Docker friendly: It's portability and single-binary design make it ideal for packaging Python environment inside containers, simplifying Docker builds and reducing image size.

Installing uv

curl -LsSf https://astral.sh/uv/install.sh | sh

Initializing Ansible Project with uv

uv init
uv add ansible
uv sync

Nomad

Install Nomad with hashi-up (Single Node)

hashi-up nomad install \
  --ssh-target-addr 192.168.1.10 \
  --ssh-target-user ubuntu \
  --ssh-target-key ~/.ssh/id_ed25519 \
  --server

Replace the IP address, username, and SSH key path with your environment details.

Enable Podman Task Driver & Raw exec plugin in Nomad

Make sure you have Podman installed. Edit the nomad configuration file (/etc/nomad.d/nomad.hcl) and add the following:

# generated with hashi-up

datacenter = "dc1"
data_dir   = "/opt/nomad"
plugin_dir = "/opt/nomad/data/plugins"

server {
  enabled          = true
  bootstrap_expect = 1
}

plugin "nomad-driver-podman" {
  enabled = true
}

plugin "raw_exec" {
  config {
    enabled = true
  }
}

client {
  enabled = true
}

Restart Nomad to apply the changes.

sudo systemctl restart nomad

Check that the Podman and Raw exec driver is enabled:

nomad node status -self -short | grep Drivers
CSI Drivers     = <none>
Drivers         = exec,java,podman,qemu,raw_exec

Deploy Traefik with Nomad and Enable Nomad Service Discovery

Create a Nomad job file (e.g., traefik.nomad) with the following content:

job "traefik" {
  datacenters = ["dc1"]
  type        = "service"

  group "traefik" {
    count = 1

    network {
      port "http" {
        static = 8080
      }
      port "admin" {
        static = 8081
      }
    }

    service {
      name     = "traefik-http"
      provider = "nomad"
      port     = "http"
    }

    task "server" {
      driver = "podman"

      config {
        image = "docker.io/traefik:v2.11.2"
        ports = ["admin", "http"]
        args = [
          "--api.dashboard=true",
          "--api.insecure=true", # Do not expose to the internet!
          "--entrypoints.web.address=:${NOMAD_PORT_http}",
          "--entrypoints.traefik.address=:${NOMAD_PORT_admin}",
          "--providers.nomad=true",
          "--providers.nomad.endpoint.address=http://${NOMAD_IP_http}:4646",
          "--providers.nomad.exposedByDefault=false"
        ]
      }
    }
  }
}

Deploy the job:

nomad job run traefik.nomad

Deploy a Demo Web Application (with Nomad Service Discovery and Traefik Routing):

Create a Nomad job file (e.g., demo-webapp.nomad) with the following content:

job "demo-webapp" {
  datacenters = ["dc1"]

  group "demo" {
    count = 3

    network {
      port "http" {
        to = -1 # Dynamic port allocation
      }
    }

    service {
      name     = "demo-webapp"
      port     = "http"
      provider = "nomad"
      tags = [
        "traefik.enable=true",
        "traefik.http.routers.demo-webapp-http.rule=Host(`demo-webapp-192-168-1-10.sslip.io`)",
        "traefik.http.routers.demo-webapp-http.tls=false"
      ]

      check {
        type     = "http"
        path     = "/"
        interval = "2s"
        timeout  = "2s"
      }
    }

    task "server" {
      env {
        PORT   = "${NOMAD_PORT_http}"
        NODEIP = "${NOMAD_IP_http}"
      }

      driver = "podman"

      config {
        image = "docker.io/hashicorp/demo-webapp-lb-guide"
        ports = ["http"]
      }
    }
  }
}

Deploy the job:

nomad job run demo-webapp.nomad

Once deployed, Traefik will automatically discover the demo-webapp service via the Nomad provider and route traffic to all running instances. You can test load balancing by sending HTTP requests to the configured hostname and port.

curl http://demo-webapp-192-168-1-10.sslip.io:8080
# Output example:
# Welcome! You are on node 192.168.1.10:20190

Repeat the request several times to see responses from different containers, indicating load balancing is working.

Tilt

Tilt is a handy tool for local development with Kubernetes. To setup a local Kubernetes cluster checkout microk8s - just make sure to enable the host-access and registry addon.

I keep an example Tiltfile project that serves as my cheatsheets whenever I need to spin up a new project. You can find it here.

Useful Resources

Mutagen

Grab the latest binary from here

Make sure you've already set up your SSH config for your remote machine. Here's an example from my ~/.ssh/config:

Host devbox
    HostName 192.168.1.100
    User jimbo

Sync files from remote to local:

mutagen sync create \
    --name=music-localdev ./localdev \
    jimbo@devbox:~/clones/music/localdev --ignore-vcs

Now you can open the local folder with any editor you like.

Port forwarding:

mutagen forward create --name=mysql tcp:localhost:3306 devbox:tcp:localhost:3306

This lets you connect to the remote MySQL server using your local MySQL client.

Using it along with WSL

My quick tips for WSL:

  • Treat WSL and Windows as separate systems
  • Access WSL files from Windows through the /wsl network drive
  • Avoid using the /mnt path in WSL for Windows files - it's slow! Use git or Mutagen instead for better performance

Useful Resources

Kubernetes (mikrok8s)

Prefer ubuntu based host for mikrok8s, you can use lima-vm or wsl2 distro. you can follow the instructions here for installing mikrok8s.

kubectl config

Make sure ~/.kube exists

mkdir -p ~/.kube

Create the config file

microk8s config > ~/.kube/config

Execute cluster-info to verify the connection

kubectl cluster-info

Microk8s addon

Enable host-access addon for convenient way to access the host from inside the cluster.

microk8s enable host-access 

Since tilt need a local registry to push the images, you can enable the local registry addon.

microk8s enable registry

later if you want to free some spaces from the registry, you can run the following command:

microk8s disable registry
microk8s disable storage:destroy-storage
microk8s enable registry 

Useful Resources

Flux

Bootstrap flux with existing git repository, image-reflector and image-automation controllers enabled for image update automation.

flux bootstrap git --url=ssh://git@<YOUR_GIT_REPOSITORY_URL> \
    --private-key-file=$HOME/.ssh/id_ed25519 \
    --branch=main \
    --path=clusters/homelab \
    --components-extra image-reflector-controller,image-automation-controller

Upgrading Flux

Update your flux cli into the latest version:

flux install \
    --components-extra="image-reflector-controller,image-automation-controller" \
    --export > ./clusters/homelab/flux-system/gotk-components.yaml

Useful Resources