Skip to content

Dispatches

vmcli: A Practical Guide for the VMware Fusion and Workstation Command Line

The command line is still the fastest way to script power state, disks, snapshots, and guest operations on with VMware Fusion (macOS) and VMware Workstation (Windows and Linux).

vmcli ships with both desktop hypervisors so you can drive the same lifecycle steps without opening the GUI.. Same lifecycle steps, no GUI.

This post covers the main modules with copy-and-paste examples.

Always confirm flags on your build with vmcli <module> --help and vmcli <vmx> <module> <command> --help, since Fusion and Workstation revisions do not always stay in lockstep.

Basic Syntax

vmcli is the cross-platform CLI for Fusion and Workstation. Each functional area (power, snapshots, storage, networking, guest operations) is a module with its own sub-commands. The usual shape is:

vmcli <vmx location> <module> <command> [args]

Important

The <vmx location> is required for commands working with VMs, and can be the first or last argument on the command line.

Global Options

Flag Description
-v, --version Display the version information.
--verbose Enable verbose logging.
-h, --help Display the help information.

Default Install Locations

CI runners and daemons often lack the same PATH as an interactive shell. Point scripts at the real binary when vmcli is not on PATH:

Platform Typical path
macOS (Fusion) /Applications/VMware Fusion.app/Contents/Public/vmcli
Windows (Workstation) C:\Program Files\VMware\VMware Workstation\vmcli.exe (or the x86 variant on some installs)

On Linux hosts, resolve the Workstation package path for your distro, or install location, then call that absolute path from automation.

If your host responds with vmcli: command not found, the binary is probably fine, but your PATH needs updating.

JSON Output for Scripting

Several query style commands accept -f json so you can parse output with jq or your language's JSON decoder instead of scraping plain text. Sub-command spelling is case sensitive (query vs Query), so match vmcli <module> --help on your build.

Example Purpose
vmcli <vmx> Power query -f json Structured power state.
vmcli <vmx> Snapshot query -f json Snapshot tree, UIDs, and current marker when the build supports it.
vmcli <vmx> Guest query -f json Guest and IP-style fields only while the VM is powered on with VMware Tools running (offline returns an error).
vmcli <vmx> Tools Query -f json Tools install and version status in JSON form.
vmcli <vmx> USB Query -f json Only on builds that list USB under vmcli --help; many Fusion vmcli trees omit USB entirely.

If -f json is missing on a sub-command, fall back to the default text layout or upgrade the desktop hypervisor build.

Core Modules and Commands

1. Power Operations (Power module)

Stop, Suspend, and Reset need -o <opType>; other rows below do not.

Command Required Args Description
vmcli <vmx> Power Start N/A Start the virtual machine.
vmcli <vmx> Power Stop -o <opType> Stop the virtual machine using the specified operation type.
vmcli <vmx> Power Pause N/A Pause the virtual machine execution.
vmcli <vmx> Power Unpause N/A Resume a paused virtual machine.
vmcli <vmx> Power Suspend -o <opType> Suspend the virtual machine using the specified operation type.
vmcli <vmx> Power Reset -o <opType> Reset the virtual machine using the specified operation type.
vmcli <vmx> Power Query N/A Query the current power state of the VM.

Power Start optional flags:

Flag Description
-p, --paused Power on in paused mode.
-s, --soft Power on in soft mode.

Valid -o opType values for Stop, Suspend, and Reset:

Value Description
hard Immediately cut power, the same as pulling the plug.
trySoft Attempt a graceful OS shutdown; fall back to hard if the guest doesn't respond.
soft Send an ACPI shutdown signal and wait for the guest to respond.
configDefault Use the operation type defined in the VM's configuration.

2. VM Management (VM & VMTemplate modules)

To create a new VM, use the VM module. Note that -g accepts a numeric preset and -c accepts any custom guest OS string.

Flag Required Description
-n, --name Yes Virtual machine name.
-d, --dirpath Yes Directory path where the VM will be created.
-g, --guesttype No Guest OS preset (numeric enum: 1–10).
-c, --custom-guesttype No Any custom guest OS type string (e.g., arm-ubuntu-64, ubuntu-64).

Example: Custom Guest OS Type

Use -c for named guest OS types such as arm-ubuntu-64 or ubuntu-64:

vmcli VM Create -n myVM -d ~/Desktop/ -c arm-ubuntu-64

The VMTemplate module is used for creating and deploying VM templates natively from the command line.

3. Snapshot Management (Snapshot module)

Snapshots give you quick checkpoints for testing and rollbacks. Commands that target an existing snapshot take a <uid> integer. Run Snapshot query first to list UIDs.

Command Required Args Description
vmcli <vmx> Snapshot Take <name> <name> Creates a snapshot. Use -d for a description and -m to include memory state.
vmcli <vmx> Snapshot Revert <uid> <uid> Revert the VM to the snapshot with the given UID.
vmcli <vmx> Snapshot Clone <uid> <filePath> <name> <uid>, <filePath>, <name> Clone a snapshot to a new .vmx file. Use -l for a linked clone.
vmcli <vmx> Snapshot Delete <uid> <uid> Removes the snapshot with the given UID. Use -d to also delete child snapshots.
vmcli <vmx> Snapshot query N/A Lists all snapshots and their UIDs.

!!! warning "Deleting and Reverting Snapshots" The VM must be powered off or suspended before using Snapshot Delete. Always run Snapshot query first to confirm the correct UID.

4. Hardware and Storage (Disk, Ethernet, Nvme, Sata modules)

Disk module. Create, extend, query, and configure virtual disks:

Sub-command Description
Disk Create -f <path> -a <adapter> -s <size> -t <type> Create a new virtual disk file.
Disk Extend <diskLabel> <newNumSectors> Extend an attached disk to a new size (in sectors).
Disk query Query the state of all disks attached to the VM.
Disk ConnectionControl Connect or disconnect a disk device.
Disk SetMode Set the persistence mode of the disk.
Disk SetReadOnly Mark a disk as read-only.
Disk SetBandwidthCap Set a bandwidth cap on the disk (bytes per second).
Disk Branch Create a new disk branch from the current VM state.

Valid -a adapter types for Disk Create: ide, buslogic, lsilogic (use lsilogic for all other types).

Valid -t disk types for Disk Create:

Type Description
0 Single growable virtual disk.
1 Growable virtual disk split into multiple files.
2 Pre-allocated virtual disk (single file).
3 Pre-allocated virtual disk split into multiple files.

Ethernet module. Configure virtual network adapters:

Sub-command Description
Ethernet query Query the current ethernet configuration.
Ethernet SetNetworkName <deviceLabel> <networkName> Connect an adapter to a named virtual network.
Ethernet SetConnectionType <deviceLabel> <connectionType> Set the connection type (e.g., nat, bridged, hostonly).
Ethernet SetVirtualDevice <deviceLabel> <deviceType> Set the virtual NIC type (e.g., vmxnet3, e1000e).
Ethernet SetPresent <deviceLabel> <bool> Add or remove a network adapter.

Other Hardware Modules:

Module Typical starting point
Nvme & Sata Controller and attachment sub-commands vary by build; see --help.
Serial Port presence and backing (file, pipe, network) via --help.

5. Configuration (ConfigParams module)

Read and write raw .vmx keys (RAM, CPU, nested virt, display name, and many hardware toggles).

Sub-command Description
ConfigParams query Print all current configuration entries for the VM.
ConfigParams SetEntry <name> <value> Write or overwrite a configuration entry.

Set bios.forceSetupOnce to TRUE for a one-time visit to firmware or EFI setup on the next boot; the hypervisor clears it after that cycle.

6. Shared Folders (HGFS module)

The Host Guest File System (HGFS) module manages the Shared Folders feature between host and guest. VMware Tools must be installed and running in the guest.

Sub-command Description
HGFS query Query the current shared folder configuration.
HGFS SetPresent <shareLabel> <bool> Add or remove a shared folder slot (for example sharedFolder0).
HGFS SetHostPath <shareLabel> <hostPath> Set the host path for that slot.
HGFS SetGuestName <shareLabel> <guestName> Name the guest sees (for example projects).
HGFS SetEnabled <shareLabel> <bool> Enable or disable a share without removing it.
HGFS SetReadAccess <shareLabel> <bool> Control read access on the share.
HGFS SetWriteAccess <shareLabel> <bool> Control write access on the share.

On many Fusion vmcli builds, HGFS sub-commands use the .vmx slot label (sharedFolder0, sharedFolder1, …), not an arbitrary string. Create the slot with SetPresent sharedFolder0 true (or matching ConfigParams entries), then set host path, guest name, and ACLs. Arbitrary labels such as projects alone are rejected until that slot exists.

7. Guest Operations (Guest module)

The Guest module executes operations directly inside a running VM. VMware Tools must be running for guest-side calls. Commands that start programs or touch the filesystem need -u <username> and -p <password>. Guest query omits -u / -p on supported builds, but the VM must still be powered on (offline returns an error). Confirm with vmcli <vmx> Guest query --help on your SKU.

Sub-command Description
Guest query Read guest state (for example OS string and networking) while the VM is on; combine with -f json for parsers.
Guest run Start a program inside the guest OS.
Guest ps List running processes in the guest.
Guest kill Terminate a process in the guest by PID.
Guest copyTo Copy a file from the host into the guest.
Guest copyFrom Copy a file from the guest back to the host.
Guest ls List directory contents inside the guest.
Guest mkdir Create a directory inside the guest.
Guest rm / rmdir Remove a file or directory inside the guest.
Guest mv / mvdir Move a file or directory inside the guest.
Guest env Show the guest's environment variables.
Guest createTempFile Create a temporary file in the guest.
Guest createTempDir Create a temporary directory in the guest.
Guest toolsproperties Show VMware Tools properties.

8. VMware Tools (Tools module)

Sub-command Description
Tools Query Query the state and version of VMware Tools in the guest.
Tools Install Mount the VMware Tools installer in the guest.
Tools Upgrade Upgrade VMware Tools to the latest available version.

9. Other Modules (MKS, Chipset, VProbes)

Module Description
MKS Mouse, keyboard, and screen (MKS) operations, including MKS captureScreenshot <output.png> while the VM displays video.
Chipset Configure low-level virtual chipset options.
VProbes Setup instrumentation probes inside the guest for performance analysis.

10. USB Devices (USB module)

Some Workstation (and occasional Fusion) builds expose a USB module. vmcli --help must list USB under Available modules; otherwise every vmcli … USB … invocation fails with Invalid/unrecognized argument "USB"`.

When the module exists:

Sub-command Description
USB Query Inspect USB devices visible to the VM; -f json when supported.
USB Connect <deviceId> Attach a host USB device to the guest.
USB Disconnect <deviceId> Release a device from the guest.

Device identifiers come from USB Query output; treat them as opaque strings.

11. Full VM clones (Clone module)

Separate from Snapshot Clone, some Workstation builds expose a top-level Clone module. Confirm with vmcli --help: many Fusion vmcli builds do not list Clone, so vmcli <vmx> Clone … fails the same way as a missing USB module.

Where Clone exists, sub-command names and flags have shifted across releases (CreateLinked, Create -t linked, and similar). Run vmcli Clone --help on the machine that will execute the job, then pin your automation to that text.

Practical Examples

Examples use VMX for the .vmx path and dirname "$VMX" for sibling files (extra disk, screenshots, logs). Use zsh or bash. On Windows, set VMX in the environment your script uses before each vmcli invocation.

!!! note "Where the .vmx path is" VM Create layout differs by product (flat folder vs *.vmwarevm bundle). Set VMX to the real .vmx after creation.

1. Create, tune, power on, optional firmware boot

vmcli VM Create -n test_vm -d ~/Desktop/ -c ubuntu-64
export VMX="$HOME/Desktop/test_vm.vmx"

vmcli "$VMX" ConfigParams SetEntry memsize 8192
vmcli "$VMX" ConfigParams SetEntry numvcpus 4
vmcli "$VMX" ConfigParams SetEntry vhv.enable TRUE
vmcli "$VMX" ConfigParams query | grep -E "memsize|numvcpus|vhv"

vmcli "$VMX" Power Start

# Optional: firmware or EFI setup on the *next* boot only (cleared after that boot)
# vmcli "$VMX" ConfigParams SetEntry bios.forceSetupOnce TRUE
# vmcli "$VMX" Power Start

2. Extra disk, JSON checks, screenshot

vmcli "$VMX" Disk Create \
  -f "$(dirname "$VMX")/test_vm-data.vmdk" \
  -a lsilogic \
  -s 50GB \
  -t 0
vmcli "$VMX" Disk query

# Same `-f json` calls as the table under "JSON output for scripting"
vmcli "$VMX" Power query -f json
vmcli "$VMX" Snapshot query -f json
vmcli "$VMX" Guest query -f json
vmcli "$VMX" Tools Query -f json
vmcli "$VMX" MKS captureScreenshot "$(dirname "$VMX")/test_vm-console.png"

3. Network (NAT to Bridged)

vmcli "$VMX" Ethernet query
vmcli "$VMX" Ethernet SetConnectionType ethernet0 bridged
vmcli "$VMX" Ethernet SetNetworkName ethernet0 "VMnet0"

4. Shared folders (HGFS)

shareLabel is the sharedFolderN slot (see the HGFS module table above).

vmcli "$VMX" HGFS SetPresent sharedFolder0 true
vmcli "$VMX" HGFS SetHostPath sharedFolder0 ~/Projects
vmcli "$VMX" HGFS SetGuestName sharedFolder0 projects
vmcli "$VMX" HGFS SetEnabled sharedFolder0 true
vmcli "$VMX" HGFS SetReadAccess sharedFolder0 true
vmcli "$VMX" HGFS SetWriteAccess sharedFolder0 true

5. VMware Tools

vmcli "$VMX" Tools Query
vmcli "$VMX" Tools Install
vmcli "$VMX" Tools Upgrade

6. Snapshots

vmcli "$VMX" Snapshot Take pre-upgrade -d "State before upgrading kernel"
vmcli "$VMX" Snapshot query
vmcli "$VMX" Power Stop -o trySoft
vmcli "$VMX" Snapshot Revert 1
# VM must be off or suspended to delete snapshots
vmcli "$VMX" Snapshot Delete 1
# When you need to drop child snapshots too, use -d before the UID (see `Snapshot Delete --help` on your build)
# vmcli "$VMX" Snapshot Delete -d 1

7. Guest Operations (Credentials)

Guest run, file copy, and ps need -u and -p (see the Guest module above). Export GUEST_PASS yourself; don't paste real passwords into shell history.

!!! warning "Credential Handling" Yes, -p on the command line is ugly. So is storing the password in a script file. For automation, use environment variables or a secrets manager and pass them at runtime instead of hard-coding them.

# Set GUEST_PASS in your environment before running these lines (never log real values).

vmcli "$VMX" Guest run -u root -p "$GUEST_PASS" \
  /bin/bash -c "apt-get update && apt-get upgrade -y"

vmcli "$VMX" Guest copyTo -u admin -p "$GUEST_PASS" \
  /path/on/host/nginx.conf /etc/nginx/nginx.conf

vmcli "$VMX" Guest copyFrom -u admin -p "$GUEST_PASS" \
  /var/log/syslog "$(dirname "$VMX")/guest-syslog.txt"

vmcli "$VMX" Guest ps -u admin -p "$GUEST_PASS"

vmcli "$VMX" Guest run -u admin -p "$GUEST_PASS" -nw \
  /usr/bin/python3 /opt/scripts/bootstrap.py

Demos

Two short screen recordings from a local Fusion Ubuntu guest (paths match a *.vmwarevm bundle on macOS). Keep "$VMCLI" quoted so VMware Fusion.app paths don't split at the space.

Snapshots, Tools, Power and Guest Query

Snapshots, Tools, Power and Guest Query

Copy Host File to Guest, then List

opy Host File to Guest, then List


Treat vmcli as a thin wrapper over desktop hypervisor operations: set VMX once per script or session, call one module at a time, and lean on --help when a flag changes between Fusion and Workstation builds.

!!! tip "Quick Reference" Run vmcli <module> --help for module-level syntax, and vmcli <vmx> <module> <sub-command> --help for a specific operation.

Disclaimer

This is not an official VMware by Broadcom document. This is a personal blog post.

The information is provided as-is with no warranties and confers no rights.

Please, refer to official documentation for the most up-to-date information.

Requests vs. HTTPX: Choosing a Python HTTP Client

requests earned its reputation honestly. For a long time, if you needed to call an API, download a file, or glue two services together in Python, requests was usually the obvious answer.

Async web frameworks, high-concurrency services, and I/O-heavy workloads changed what many teams need from an HTTP client. That's where httpx starts to matter.

This post compares requests and httpx from the point of view of a Python developer who already knows requests, wants a clear explanation of what httpx adds, and doesn't want hand-wavy "modernization" advice.

The Champion: requests

requests became the standard because it removed friction. Small API. Sensible defaults. Session when you need reuse.

  • The API is small and easy to learn.
  • The defaults are simple enough for scripts and internal tools.
  • Session gives you connection reuse and shared configuration without much ceremony.
  • The ecosystem around it is huge: tutorials, examples, blog posts, Stack Overflow answers, and third-party integrations all assume you know requests.

For synchronous Python code, it's still a good library. A lot of production code doesn't need async support, doesn't need HTTP/2, and doesn't need a broader transport model. In those cases, requests remains a sensible choice.

That's why this comparison is not really about replacing a bad tool. It's about recognizing where the older tool stops fitting as cleanly.

The Challenger: httpx

httpx looks familiar on purpose. The synchronous API feels close enough to requests that most Python developers can read it immediately. That compatibility is one of its best features, because it lowers the cost of trying it.

Its two biggest advantages are:

  • Native async and await support through httpx.AsyncClient
  • Optional HTTP/2 support on both sync and async clients

Those aren't cosmetic differences.

Async support means httpx fits naturally into frameworks like FastAPI, Starlette, and any other async application that should not block the event loop on outbound network calls. HTTP/2 support means one client connection can handle multiplexed requests when the remote server supports it, which can matter for high-concurrency workloads.

You opt in with httpx.Client(http2=True) or httpx.AsyncClient(http2=True), and the server still has to support HTTP/2 for you to get it.

httpx also pushes users toward safer networking defaults. The biggest example is timeouts: requests doesn't time out unless you ask it to, while httpx enforces timeouts by default.

Decoding the Major and Minor Pentatonic Scales

If you've ever felt trapped inside the classic minor pentatonic box, you're not alone. The good news is that the box isn't the problem. The real breakthrough comes when you understand what the notes inside that shape are doing, and that's the moment the fretboard starts making sense.

Most intermediate players start in the same place: Minor Pentatonic Position 1, fingers parked at the 5th fret, trying to find fresh ideas out of the same old lick. While that scale shape is useful, but it can start to feel a bit clostrophobic.

My goal with this post is to help you:

  • Stop seeing pentatonic scales as random patterns.
  • Start seeing them as intervals, Root Notes, and musical colors.
  • Learn why major and minor pentatonic scales are deeply connected.
  • Use that connection to make better phrases anywhere on the neck of the guitar.

Tip

You don't need to memorize five more disconnected shapes to improve. You need to understand what your current shape is already showing you.

The word pentatonic just means five notes.

That's the whole idea:

  • A major scale has 7 notes.
  • A natural minor scale has 7 notes.
  • A pentatonic scale trims that down to 5.

Why remove two notes?

Because the notes that are left out are the ones that create the strongest half-step tension. Whilst half-steps aren't bad, they're the notes most likely to sound tense, crunchy, or like they want immediate resolution. When you remove them, the scale becomes smoother and more forgiving.

That's why pentatonic scales are awesome:

  • They're easy to hear.
  • They're easy to phrase.
  • They sit well over a range of chords.
  • They quickly sound musical, even before your theory knowledge is deep.

On the guitar, the pentatonic scales work so well because they remove some of the strongest tension notes from the full scale. You can still create emotion and movement, but you're less likely to land on a note that feels harsh or unresolved by accident.

Why Pentatonics Feel So Friendly

By removing some of the most tension-heavy notes from the full 7-note scale, pentatonics give you a leaner set of notes that is easier to phrase with confidence. That's why they're often the first scale family guitar players truly learn to hear.

Hay for the Heifers

Reflection

Farm work looks noble from a distance, viewed through the softening lens of years gone by.

But it was just sweat, salt, and the inescapable gravity of a bloodline leading ahead of us on a rusted tractor seat. It was the kind of grit that settled into our joints and became part of our posture, a permanent inheritance.

It was an education in endurance, watching our Grandad’s back silhouette against the suffocating heat haze while my brother and I moved in a wordless, desperate rhythm below, reading each other's every breath because there wasn't room for anything else.

We learned early on that love between us wasn’t spoken. It was thrown, caught, stacked, and suffered through together. Yet, there was a heavy, almost sacred grace found at the barn at dusk, when the Massey finally choked out, the dust began to sink, and the pine shadows stretched long enough to let us finally clear our lungs.

This song is a reminder of those long afternoons left behind, side-by-side.

Lyrics

HAY FOR THE HEIFERS

The sun’s a red eye peeking o’er the Georgia pines
Grandad’s on the seat, keeping everything in line
That Massey belching out the blackest of smoke
If this baler misses ties, boys, it ain't no joke
The peanut vines are cured, they’re gold and dry
We’re kicking up a grit-cloud reaching for the sky

     Baling square bales in the heat and the haze
     Chasing down the harvest in a dusty-red daze
     Stack ‘em on the trailer, keep the corners tight
     Building up a mountain ‘neath the afternoon sky
     It’s hay for the heifers, it’s gold for the herd
     Sweating ‘til we’re finished, Lord, you have my word

That trailer’s getting long and it’s sagging in the middle
The dry vines are humming like a low-tuned fiddle
I’m standing on the edge, catching the bails on the fly
Dust in my throat and a sting in my eye
Grandad shifts a gear, we feel a jerk and a sway
We’re heavy-loaded, headed for the barn today

Back at the barn, the shadows stretching long and lean
The sweetest smelling hay-house that you’ve ever did seen
Back that Massey up, let the iron engine roar
We’ll stack ‘em to the rafters, all the way from the floor
The cows are in the lot and they’re bellerin’ low
They know that candy’s got the winter-time glow

     Repeat Chorus

Yeah, stack ‘em high
Keep 'em square
Oh, that hay is everywhere
It’s hay for the heifers, gold for the herd

Writer: J. Ryan Johnson (BMI)
Copyright: © 2026 J. Ryan Johnson. All rights reserved.
Phone: +1 (407) 902-5419
Email: hello [at] tenthirtyam [dot] org

Audio Disclaimer

Lyrics: Original | Audio: AI-Generated

I am a songwriter and a musician, but I am not the voice meant to inhabit these verses.

I've used AI to bridge the gap for the concept demos, crafted to serve as blueprints that capture the genre, tone, and weary soul I hear for each song.

They exist as an invitation, offered in the hope that these lyrics will eventually reach the hands of an artist and storyteller who can bring them fully into the light.

Until then, they remain as they were born: quiet reflections on the grit and grace found just north of the county line.

Nginx: A Practical Deep Dive

Nginx ends up in front of a surprising amount of web traffic. You might not think much about it until you have to troubleshoot a redirect loop, a broken certificate renewal, or an application that only fails once it sits behind a proxy.

Static sites, application servers, APIs, internal tools, container platforms, and CDN origins often have Nginx somewhere out front. People keep using it because it's fast, it's predictable, and it makes you declare what the server should do.

This guide walks through the part that matters in practice: what Nginx is good at, how its config model works, how to install it, how to serve a site, how to terminate TLS, how to replace .htaccess style behavior the Nginx way, and how to use it as a reverse proxy and a basic load balancer.

Nginx is a web server, reverse proxy, and load balancer. In a modern stack it usually does one or more of these jobs:

  • Serves static files directly.
  • Terminates HTTPS connections.
  • Redirects HTTP to HTTPS.
  • Proxies requests to application servers.
  • Balances traffic across multiple backend services.
  • Adds request and response headers.
  • Buffers slow clients away from backend applications.
  • Enforces simple access control rules.
  • Caches upstream responses when configured to do so.

It's especially good at static assets, reverse proxying, and a lot of concurrent connections. It also works well as the boring edge layer in front of applications written in Node.js, Python, Go, Ruby, PHP, Java, or anything else that can listen on a port.

The simplest mental model:

graph TD
    A[Client Browser] -->|HTTPS Request| B[Nginx]
    B -->|HTTP Request to Local App| C[Backend Application]

Nginx does not need to understand your application. It just needs to accept the request, apply the rules you gave it, and either serve the response or pass the request upstream.

Nginx became popular because it handles several common infrastructure problems without much drama:

  • It handles many concurrent connections efficiently.
  • It serves static files quickly.
  • It's excellent as a reverse proxy.
  • It's good at shielding backend apps from awkward client behavior.
  • It centralizes TLS, redirects, headers, and access control.
  • It can load balance traffic without a separate appliance.
  • Its configuration is explicit and reviewable.

Nginx expects configuration in known files, not hidden per-directory overrides inside the document root. That makes behavior easier to inspect, version, test, and deploy.

VMware Workstation and Fusion 26H1 Release

VMware Workstation Pro and Fusion Pro 26H1 shipped recently and was a fairly quiet release.

The biggest change in Workstation Pro 26H1 is the move to a fully 64-bit architecture on Windows. Operating systems and hardware moved on from 32-bit limitations a long time ago, but parts of Workstation's underlying components were still dragging legacy dependencies forward.

The 26H1 release finally drops them. Installers, services, libraries, and application binaries now operate entirely in a 64-bit environment. It's a cleanup that eliminates years of technical debt and gets out of the way of heavy edge-case workloads on the Windows side.

In 26H1, you can finally see when a virtual machine was created and when it was last powered on.

If you manage a handful of virtual machines, you probably won't care. If you manage dozens, or maintain a sprawling cybersecurity lab, this is a massive quality-of-life fix. It's significantly easier to find dormant lab machines without having to boot them or guess based on filesystem modification dates.

The most impactful part of 26H1 isn't technical. Since Workstation Pro and Fusion Pro are now free for personal, educational, and commercial use, the economics of local virtualization are completely different. You don't have to justify a license request or fall back to an open-source alternative with fewer features. You can just download the premier desktop hypervisor and run your lab.

The 26H1 release includes a typical collection of bug fixes and security updates to improve platform stability. When you rely on virtual machines as part of your daily workflow, predictable execution matters more than new knobs to turn.

Disclaimer

This is not an official VMware by Broadcom document. This is a personal blog post.

The information is provided as-is with no warranties and confers no rights.

Please, refer to official documentation for the most up-to-date information.

How I'm Preparing for GH-600, GitHub's Agentic AI Developer Certification

GitHub Certification

When I published Branching Out: GitHub Certification Path, GitHub had five certifications. That list is already out of date, and so is my own exam roadmap.

GitHub has now added a sixth exam: GH-600, GitHub Certified: Agentic AI Developer (beta). As of June 6, 2026, this one is still in beta, and it fills a different role than the existing Copilot certification. The Copilot exam is about using GitHub's AI tooling well. GH-600 is about building, operating, constraining, and evaluating agents inside real software delivery workflows.

I'm treating this post as my preparation plan for GH-600. These are the official materials, docs, and hands-on exercises I'm using to get ready for it.

GitHub is treating agentic AI as an engineering discipline: tool access, MCP configuration, execution boundaries, memory, observability, evaluation, and guardrails. That's the right frame for the technology, and it's the reason this exam is worth taking seriously.

Versioning Documentation with ProperDocs and mike

If you're using ProperDocs, versioned publishing should be straightforward. Build the docs for a release, publish them under a stable version label, and leave older versions alone. Until mike v2.2.0, that simple workflow still carried a nagging question: did the rest of the tooling really understand ProperDocs, or was it still assuming MkDocs under the hood?

mike publishes each supported release as its own directory tree on a deployment branch, usually gh-pages. Old versions stay put even when today's main branch moves on. In the mike v2.2.0 release, that process got simpler for ProperDocs users: when the properdocs package is installed, mike runs properdocs instead of mkdocs. No separate workflow branch. No wrapper script. No extra switch to remember.

The release notes include:

Add support for ProperDocs.

In practice, that means mike now checks for the properdocs package first. When it's present, mike uses the properdocs command and includes properdocs.yml and properdocs.yaml in its default configuration file search order.

That is exactly the kind of compatibility work you want in a documentation pipeline. Install the generator you actually use, install mike, and let tool detection do the boring part correctly.

ProperDocs exists because some teams need the MkDocs 1.x operating model to keep working. They have plugins, theme overrides, snippets, macros, and workflow assumptions built around that ecosystem. Versioned publishing shouldn't force those teams into a second set of build logic.

Before this change, the friction wasn't that versioned docs were impossible. The friction was that ProperDocs users had to think too much about whether the surrounding tooling still assumed MkDocs. That extra uncertainty is how CI jobs grow unnecessary conditionals and side paths.

With mike v2.2.0, the command flow is really simple:

python -m pip install "properdocs" "mike>=2.2.0"
mike deploy 1.0 latest --push

PowerShell Command Naming: Approved Verbs, Clear Nouns, and Better APIs

PowerShell PowerShell

The difference between a PowerShell command that feels native and one that feels bolted on is often not the implementation. It's the name. In PowerShell, names are part of the interface contract: they drive discoverability, shape user expectations, and determine whether your module behaves like a first-class citizen or like a private script someone published by accident.

Official References

Start with the official documentation for approved verbs, Get-Verb, and the PSScriptAnalyzer rule UseApprovedVerbs.

If you maintain PowerShell modules long enough, you start to see the same mistakes over and over:

  • Functions named with verbs PowerShell doesn't recognize
  • Plural nouns where PowerShell expects singular object names
  • Verb choices that imply one behavior while the function performs another
  • "Do everything" verbs like Invoke used as a substitute for design
  • State-changing commands that don't support -WhatIf and -Confirm

This post is about avoiding those mistakes and building command names that feel idiomatic, predictable, and maintainable.

A bad command name spreads farther than you might expect. It shows up in documentation, examples, tab completion, issue reports, code review comments, CI logs, and muscle memory. Once users learn the wrong name, you either carry it forward forever or you break them later when you fix it.

PowerShell isn't just a shell. It's a command discovery environment. Users don't need to remember your entire module if your names fit the ecosystem. They can find commands through patterns:

Get-Command -Verb Get
Get-Command -Module My.Module
Get-Command -Noun VcfCluster
Get-Help Get-VcfCluster -Full
Get-Verb

That only works well when command names follow the conventions the engine, the help system, and the user all expect.

The best PowerShell command names are easy to guess. If I know your module has a cluster object, I should be able to guess that retrieval is probably Get-Thing, creation is probably New-Thing, mutation is probably Set-Thing, deletion is probably Remove-Thing, and validation is probably Test-Thing. If your module breaks that expectation, you increase the amount of documentation the user must read before they can do anything useful.

Seminole Clay

'73 Alday Murders

The Alday murders didn’t end at the graveside in May 1973. They cast a long shadow over Seminole County, Georgia, forever altering the lives of those left behind and reshaping the community’s very character. Six members of the Alday family were brutally taken at once, leaving their survivors grappling with profound grief and the weight of seeing their family name forever entwined with tragedy. The farm that had sustained generations eventually passed from the family’s hands, a somber reminder of the devastation wrought by the killings.

The murders shattered the sense of rural security that had once prevailed. Neighbors learned to lock their doors with newfound anxiety, and a community accustomed to trust found itself living in a state of suspicion and fear.

As the years stretched into decades of trials, appeals, and public debate, the wounds of the Alday murders remained open and festered, fueled by frustrations over the slow pace of justice.

The events of the Alday murders continue to resonate in South Georgia’s collective memory. The Alday name has transcended its historical significance, becoming a symbol of loss, a testament to the weathered state of the community, and a haunting reminder of the enduring impact that tragedies can have on lives.

Lyrics

SEMINOLE CLAY

The hay was heavy, the humidity high
Just a Monday under a South Georgia sky
A quiet dirt road, nothing out of place
While strangers moved silent through borrowed space
They’d come down south from a prison line
Three from Maryland, leaving the law behind
A stolen car cooling in the yard that day
While the red dust hung and the fields stood gray

     And the wind don’t blow through the wiregrass no more
     Not the way that it used to blow before
     There’s a stain on the harvest, a shadow on the sun
     And the devil used a stranger’s hand to hold a gun
     You can plow the earth and pray for rain to fall
     But there’s six empty chairs against the kitchen wall
     Yeah, the blood runs deep in the Seminole clay

They were fathers, they were brothers, they were working men
Walking through the doorway like they’d always been
One by one, they were taken inside
Bound by fear with nowhere to hide
Then Mary pulled in, never turned away
She was taken from her home that afternoon in May
They carried her out where the pinewoods stand
And left her with the fire ants on the family land

We used to leave the keys inside the truck
We used to trust in God and trust in luck
But luck ran out in May of ‘73
And it never came back to this county

     Repeat Chorus

The trials ran long, and the years ran slow
Through the appeals, the cells, and the death-row
Some met the lightning or the needle's sting
But a verdict don't bring back the breath of things
No war was declared, no sirens gave a sound
Just six good souls laid in the cold, hard ground

So if you pass through Seminole, tread lightly evermore
Cause the ghosts are waiting by that trailer door
Yeah, the blood runs deep
In the Seminole clay

Writer: J. Ryan Johnson (BMI) Copyright: © 2026 J. Ryan Johnson. All rights reserved. Phone: +1 (407) 902-5419 Email: hello [at] tenthirtyam [dot] org

Audio Disclaimer

Lyrics: Original | Audio: AI-Generated

I am a songwriter and a musician, but I am not the voice meant to inhabit these verses.

I've used AI to bridge the gap for the concept demos, crafted to serve as blueprints that capture the genre, tone, and weary soul I hear for each song.

They exist as an invitation, offered in the hope that these lyrics will eventually reach the hands of an artist and storyteller who can bring them fully into the light.

Until then, they remain as they were born: quiet reflections on the grit and grace found just north of the county line.