Cheat Sheets

wslc Cheat Sheet: Commands, Compose and Docker Mapping (PDF)

wslc lets you run Linux containers on Windows 11 straight from PowerShell, with no Docker Desktop and no distro to manage. Most of its commands read like Docker commands, so this wslc cheat sheet focuses on the places where they don’t: compose, prune, flags that are missing and a config file that isn’t called config.

Original content from computingforgeeks.com - post 182168

You get every wslc command grouped by task, a wslc vs docker mapping table, the honest wslc compose status and the real output of each subcommand. We checked every command in November 2026 on a Windows 11 24H2 lab VM with WSL and wslc 3.0.3, and we read each flag from wslc <command> --help on that box rather than from Docker habits.

wslc cheat sheet: commands by task

Every row below ran on our lab VM as a normal admin user, using the names from this page (web, app, db, appnet, pgdata). Most ran through a cmd.exe capture script, and we repeated the core run, ps, exec, network and prune flow in Windows PowerShell, where only the quoting of exec one-liners behaved differently. Commands that ask for confirmation are shown with -f, because a prune without it waits for a y/N answer and hangs any script that runs it. The tables are also the content of the PDF, so what you print is what we tested.

Version, info and sessions

CommandWhat it does
wsl --versionWSL, kernel and Windows build numbers
wslc versionPrint the wslc CLI version
wslc infoClient versions, settings file path and running sessions
wslc system session listList session VMs for your user
wslc system session terminateStop the session VM; the next command boots a fresh one
wslc --helpList every top-level command

Images and builds

CommandWhat it does
wslc pull nginx:latestPull an image from Docker Hub
wslc imagesList images (same as wslc image ls)
wslc images --digestsList images with their sha256 digests
wslc build -t hello:1.0 .Build the Dockerfile in the current folder and tag it
wslc tag nginx:latest web:testAdd a second tag to an image
wslc rmi web:testRemove an image or one of its tags

Run containers

CommandWhat it does
wslc run --rm alpine:latest cat /etc/os-releaseOne-off container, removed when it exits
wslc run -d --name web -p 8080:80 nginx:latestBackground container on 127.0.0.1:8080
wslc run -d --name web -p 0.0.0.0:8080:80 nginx:latestPublish on every host interface
wslc run -d --name db --network appnet -e POSTGRES_PASSWORD=labpass -v pgdata:/var/lib/postgresql postgres:18User network, env variable and named volume
wslc start webStart a stopped container
wslc restart webRestart a container

List, logs, exec and stats

CommandWhat it does
wslc psRunning containers (aliases: wslc list, wslc ls)
wslc ps -aAll containers, including stopped ones
wslc ps --format jsonOne JSON object per container
wslc logs webPrint the logs of a container
wslc logs --tail 2 -t webLast 2 lines with timestamps
wslc exec web nginx -vRun a command in a running container
wslc exec -w /usr/share/nginx/html web ls -lRun a command in a chosen working directory
wslc stats webOne snapshot of CPU, memory, network and block I/O
wslc inspect --format json dbContainer details as single-line JSON
wslc container cp web:/etc/nginx/nginx.conf .Copy a file out (there is no top-level wslc cp)

Stop and remove

CommandWhat it does
wslc stop webSend the image STOPSIGNAL (SIGTERM if none), then kill after the timeout
wslc stop -t 2 webWait only 2 seconds before killing
wslc rm webRemove a stopped container (aliases: remove, delete)
wslc rm -f app dbStop and remove running containers in one step
wslc rm -v webAlso remove the anonymous volumes of the container

Networks and volumes

CommandWhat it does
wslc network create appnetUser network with name-based DNS between containers
wslc network lsList networks (bridge, host and none exist by default)
wslc network connect appnet webAttach a running container to a network
wslc network rm appnetDelete a network
wslc volume create pgdataNamed volume inside the session disk
wslc volume lsList volumes and their driver
wslc volume rm pgdataDelete a volume and its data

Prune (cleanup)

CommandWhat it does
wslc container prune -fDelete all stopped containers without a prompt
wslc image prune -fDelete dangling images only
wslc image prune -a -fDelete every image no container uses
wslc volume prune -fDelete unused anonymous volumes only
wslc volume prune -a -fAlso delete unused named volumes
wslc network prune -fDelete networks no container uses

Settings (the wslc config file)

CommandWhat it does
wslc settingsCreate settings.yaml on first run and open it in the default editor
wslc settings resetOverwrite settings.yaml with the commented defaults
Get-Content $env:LOCALAPPDATA\wslc\settings.yamlPrint the settings file in PowerShell

Two rows trip up nearly everyone coming from Docker. The publish row binds to 127.0.0.1 unless you add 0.0.0.0:, and the volume prune row leaves named volumes alone unless you add -a. Both are covered with real output further down, along with the timings that make the first command of a session feel broken when it isn’t.

How do wslc commands map to docker?

For day-to-day container work, swap docker for wslc and most commands run unchanged, because wslc drives a standard container engine inside its session VM. The differences sit in the client, and they are the ones that break scripts copied from a Linux box. This table lists what changed when we replayed Docker habits against wslc.

Docker commandwslc equivalentWhat is different
docker pswslc psAlso wslc list and wslc ls; same -a, -q and –format json flags
docker run -p 8080:80wslc run -p 8080:80Binds 127.0.0.1 unless you write 0.0.0.0:8080:80
docker compose up -dNoneUnrecognized command: ‘compose’
docker system pruneNoneRun the container, image, volume and network prunes one by one
docker cpwslc container cpNo top-level cp in this release
docker inspect -f '{{.State.Status}}' dbwslc inspect --format json dbA Go template fails: Supported format types are: json
docker run --platform linux/arm64NoneOption name was not recognized
docker stats --no-streamwslc statswslc stats prints one snapshot and exits
docker logs -f webwslc logs -f webSame -f, -n, -t, –since and –until flags
docker versionwslc versionNo docker.exe shim is installed

The missing pieces decide whether wslc can replace Docker for you, so check your own workflow against this list before you uninstall anything.

  • Your scripts call only run, ps, logs, exec, build, pull and the network and volume verbs.
  • No tool in your build talks to a Docker socket, because wslc exposes no docker.exe and no DOCKER_HOST.
  • You can live without a compose.yaml for now, or your stack is two or three services.
  • Every image you run publishes an amd64 build, since –platform is rejected.

If all four hold, wslc covers your day and you can drop the Docker Desktop licence question entirely. If one fails, keep Docker Engine in a WSL distro alongside it, and keep our Docker CLI and Compose cheat sheet open for that side. Teams that prefer daemonless containers can compare the Podman commands too, since Podman also runs on Windows through WSL. If speed decides it, our wslc vs Docker benchmark timed cold start, memory, file I/O, builds and PostgreSQL start-up on the same Windows 11 machine. For the concepts behind images, layers and networks, which carry over to wslc unchanged, the Nigel Poulton book Docker Deep Dive (paid link) (2025 edition, Kindle) is the one we hand to people new to containers.

Does wslc compose work yet?

No. wslc 3.0.3 has no compose command, so wslc compose up fails at the parser before anything reaches the session VM. We tried it from a folder holding a one-service compose.yaml.

wslc compose up -d

The answer is the same message any unknown verb gets, followed by the full command list:

Unrecognized command: 'compose'

Usage:  wslc [global-options] [<command>] [options]

Plain docker compose fails too, because the WSL package installs no docker.exe. The capture below, taken in Command Prompt, shows the compose.yaml, both failures and the command list that wslc prints instead.

wslc compose up -d and wslc compose version returning Unrecognized command compose, and docker compose not recognized in Command Prompt

Compose work is happening upstream. The WSL repository has a feature/compose branch, and Microsoft merged the latest master into it on October 16, 2026 (PR 41893). No stable or preview release ships it yet, so until one does, a small stack is a handful of wslc commands in the order compose would run them.

wslc network create appnet
wslc volume create pgdata
wslc run -d --name db --network appnet -e POSTGRES_PASSWORD=labpass -v pgdata:/var/lib/postgresql postgres:18
wslc run -d --name app --network appnet alpine:latest sleep 3600

Service discovery is the part people worry about, and it works on a user network. From inside app, the name db resolves and answers a ping:

wslc exec app getent hosts db

The address belongs to the postgres container on appnet.

172.18.0.2        db  db

Test with getent hosts, not nslookup. The busybox nslookup in Alpine appended the DNS search suffixes the container inherited from our LAN and returned NXDOMAIN for every one of them, which looks like broken DNS when it isn’t. The network matters too: two alpine containers started without --network landed on the default bridge, and getent hosts between them found nothing and exited with code 2. The full exchange on appnet, with the ping and both containers up, looked like this.

wslc network create appnet, volume create pgdata, postgres and alpine containers on appnet, then getent hosts db and ping db

What you give up without compose is everything that lives in the YAML. Before you script a stack by hand, check that these three gaps are acceptable for your project:

  • Your app retries its database connection, because there is no depends_on and the postgres:18 pull plus first start took 125.5 seconds on our VM.
  • No two checkouts on the machine create the same appnet and db names, since nothing prefixes names per project and they collide in one session.
  • Someone owns the PowerShell file that replaces up and down, so it does not drift from the compose.yaml your team keeps for Docker.

For two or three services we would script it that way. For more, stay on Docker Compose until wslc ships its own, because a hand-written start order breaks the first time a service is added.

How do you run and inspect a container with wslc?

Use wslc run -d with a name and a published port, then check it with wslc ps. Expect the first command of a new session to be slow, because wslc creates and formats the session VM before it pulls a single layer. Our first wslc pull nginx:latest took 208.6 seconds; the same 163MB image, pulled by wslc run on an already running session, later took 83.3 seconds, so roughly two minutes of that first wait was the session start. Once the image was local, wslc run -d returned in 2.4 seconds. The session also shuts down after 30 seconds idle by default, so a gap between test runs brings part of that cold start back.

wslc run -d --name web -p 8080:80 nginx:latest

List it and read the PORTS column, which is where wslc parts ways with Docker.

wslc ps

The port is bound to loopback only, so the page answers on localhost but not from another machine on your network:

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS         PORTS                    NAMES
9edce8831513   nginx:latest   "/docker-entrypoint.…"   25 seconds ago   Up 9 seconds   127.0.0.1:8080->80/tcp   web

We would keep that default on a laptop, because a database published with -p 5432:5432 stays off the coffee-shop Wi-Fi. If you do need the container reachable from the LAN, write -p 0.0.0.0:8080:80; our run with that flag showed 0.0.0.0:8080->80/tcp in the same column. Logs, exec and stats then behave the way Docker users expect.

wslc logs --tail 2 -t web

Each line carries an RFC3339 timestamp from the container runtime, and the first one is our own curl request.

2026-11-01T05:19:18.857832000Z 169.254.73.254 - - [01/Nov/2026:05:19:18 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.21.0" "-"
2026-11-01T05:19:18.285482300Z 2026/11/01 05:19:18 [notice] 1#1: start worker process 32

Look at the client address: 169.254.73.254, not 127.0.0.1. Requests from Windows reach the container through the network relay of the session, so access logs and IP allowlists inside the container see that link-local address instead of loopback. If an app only trusts 127.0.0.1, it will reject your browser until you allow that range. To run commands inside the container, pass its name to wslc exec.

wslc exec -e GREETING=karibu web sh -c 'echo $GREETING from $(hostname)'

The variable and the container hostname both come back from inside the container.

karibu from 9edce8831513

Use single quotes in PowerShell. With double quotes, PowerShell expands $GREETING and $(hostname) on the Windows side before wslc sees them, so the same command printed from win11-lab: an empty variable and the name of the Windows machine instead of the container. Command Prompt passes the dollar signs through untouched, which is why Linux one-liners copied into cmd.exe work with double quotes and break when you paste them into PowerShell.

The window below holds the whole session, including wslc stats web. That command printed one snapshot and returned, so you don’t need the --no-stream flag from Docker in scripts. The memory limit of 5.778GiB is what the session VM got from the 12 GB we gave the lab VM, which matches the documented default of half the memory Windows reports.

wslc run -d publishing nginx on 127.0.0.1:8080, wslc ps, wslc exec with single and double quotes in PowerShell, and wslc stats

For scripts, wslc ps --format json prints one JSON object per container with State, Ports and Networks fields. When we attached web to appnet with wslc network connect, its Networks field read appnet,bridge, which is a quick way to confirm a container joined the network you meant. wslc inspect --format json gives the full record, but --format takes only json: a Docker-style --format '{{.State.Status}}' failed with “Invalid format value”. Pipe it into ConvertFrom-Json in PowerShell instead of reaching for a Go template.

How do you remove containers and prune with wslc?

Use wslc rm -f for running containers and the four separate prune commands for cleanup, always with -f in scripts. A plain wslc rm on a running container refuses with a named error code.

wslc rm web

The error tells you both ways out, stop first or force it:

Container '9edce8831513b6683993a7f8e5e2ad0338ca59eeafe5daa0801ea16fd5d69b36' is running and cannot be removed. Either stop the container before removing or use forced remove (-f).
Error code: WSLC_E_CONTAINER_IS_RUNNING

Forcing it with wslc rm -f web took 4 to 6 seconds across our runs. A plain wslc stop can take longer than you expect, because it sends the stop signal of the image (SIGTERM unless the image sets another) and waits for the process to exit. Our alpine container running sleep 3600 ignored the signal, so wslc stop app sat out the timeout, took 16 seconds and ended with exit code 137. Pass -t 2 when you do not care about a clean shutdown, and leave it off for databases.

wslc rm failing with WSLC_E_CONTAINER_IS_RUNNING, then wslc rm -f web and an empty wslc ps -a in PowerShell

Pruning is where wslc and Docker differ the most. There’s no wslc prune and no wslc system prune, and both return “Unrecognized command: ‘prune'”. Instead you get one prune per object type, and each of them asks for confirmation unless you pass -f.

wslc container prune

Run from a scheduled task with no console, the prompt below sat for 81 seconds until we killed the process. A CI job or a login script would hang the same way, and nothing in the output tells you why.

WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N]

The volume prune is the one that surprises people. With the db container gone, wslc volume prune -f reclaimed 0B and left pgdata in place, because without -a it only removes anonymous volumes. Adding -a deleted pgdata and reclaimed 40.72MB, which was the whole PostgreSQL data directory. A second run in PowerShell, below, shows the other half: -f alone removed the anonymous volume left by an earlier postgres container that ran without a named volume and was removed without rm -v, and it still kept pgdata. That split is the safer design, but a cleanup script written for the old Docker behaviour will quietly keep every named volume.

wslc volume prune -f removing only an anonymous volume and keeping pgdata, volume prune -a -f deleting pgdata, network rm and container prune -f

The same split applies to images. wslc image prune -f found nothing dangling and reclaimed 0B, while wslc image prune -a -f untagged alpine, hello, postgres and nginx and freed 541.1MB. Run the four prunes in this order when you clean up a session.

  1. Run wslc container prune -f and check that wslc ps -a lists only containers you want.
  2. Run wslc network prune -f and check that wslc network ls shows bridge, host and none.
  3. Run wslc image prune -a -f and check that wslc images is empty or holds only images in use.
  4. Run wslc volume prune -a -f last, and only after copying out any data you need.

Volumes go last because they’re the only step you can’t undo with a pull. A guest volume lives inside the storage.vhdx of the session, not as files in Explorer, so copy data out with wslc container cp before you prune. In our run that copy took 1.2 seconds for nginx.conf, so there is no reason to skip it.

Where is the wslc config file?

There is no wslc config command; it returns “Unrecognized command: ‘config'”. Configuration lives in %LOCALAPPDATA%\wslc\settings.yaml, and wslc settings manages it. On our VM the file did not exist until the first wslc settings run, which created it at 1,427 bytes and opened it in Notepad. Microsoft documents that it uses your default editor for .yaml files and falls back to Notepad when none is associated, which is what a clean Windows install has.

wslc settings

Every key ships commented out at its default. These are the session keys from the generated file, with the defaults the file itself states.

session:
  # Number of virtual CPUs allocated to the session (e.g. 4 default: all available CPUs)
  # cpuCount: default

  # Memory limit for the session (e.g. 2GB default: half of available memory)
  # memorySize: default

  # Maximum disk image size (e.g. 500GB default: 1TB)
  # maxStorageSize: default

  # Default host address that published ports bind to when 'container run -p' is
  # used without an explicit address (default: 127.0.0.1)
  # defaultBindingAddress: default

  # DNS name that resolves to the host loopback address (default: host.wslc.internal).
  # Set to "none" to disable the entry.
  # hostLoopback: default

  # Seconds an idle session VM stays running before it is torn down (default: 30)
  # idleTimeout: default

The file also has a storagePath key for moving the session disk off C:, and a top-level credentialStore that defaults to wincred. These are the three we would look at first.

  • idleTimeout is raised above 30 seconds if you hit cold starts between test runs.
  • memorySize is set explicitly on a 16 GB laptop, so the session cannot take 8 GB from your IDE.
  • defaultBindingAddress stays at its default unless every container you run should face the LAN.

The session keys are read when a session starts, so restart it after an edit; defaultBindingAddress and credentialStore are read on every wslc command, according to the WSL container settings reference. wslc info confirms which file the client reads, Get-Content prints it, and wslc system session terminate stops the VM so the next command boots it with the new values.

wslc info showing the settings.yaml path, Get-Content of settings.yaml, then wslc system session terminate leaving no sessions

One thing went wrong here. After the terminate, our capture script started wslc settings in the background, which returned after 28.4 seconds, and a few seconds later ran wslc network create appnet. That network create failed after another 28.4 seconds with “Unspecified error” and code E_FAIL, and the next two commands reported that appnet did not exist. The most likely cause is two wslc processes bringing up the new session at once, although we could not prove it. In the same session, a later wslc network create appnet succeeded in 13.3 seconds. If a command fails like that just after a terminate, run wslc system session list, wait for the session to show up, and retry. According to the settings reference, a bad value in settings.yaml does not stop the session either; wslc prints a warning with the file path and line number and falls back to the default (we did not test that case), and wslc settings reset restores the commented template.

How do you install wslc on Windows 11?

wslc ships inside the WSL package, so installing or updating WSL gives you the CLI. On a machine that already runs WSL from the Microsoft Store, wsl --update is the documented route. Our VM was a clean Windows 11 install, so we enabled the two Windows features from an elevated PowerShell prompt first.

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

Then we downloaded the 367 MB MSI from the latest stable GitHub release with curl.exe and installed it silently. The API lookup returned 3.0.3 on our VM, the install finished in about a minute and a half, and the reboot activates the two features.

$tag = (Invoke-RestMethod https://api.github.com/repos/microsoft/WSL/releases/latest).tag_name
curl.exe -sSL -o $env:TEMP\wsl.msi "https://github.com/microsoft/WSL/releases/download/$tag/wsl.$tag.0.x64.msi"
Start-Process msiexec.exe -ArgumentList "/i $env:TEMP\wsl.msi /qn /norestart" -Wait
Restart-Computer

After the reboot, both version commands should report the same release, here in Command Prompt. A VM needs nested virtualization (CPU type host on Proxmox) for the session VM to start.

wsl --version reporting WSL 3.0.3 and wslc version 3.0.3 in Command Prompt on Windows 11

You do not need a WSL distro for any of this. wslc runs its own session VM, separate from Ubuntu or the RHEL and Fedora images for WSL you may already use. Our hands-on WSL containers guide goes further into how the session VM is built, Dockerfile builds with health checks, virtiofs mounts and a file I/O benchmark against Docker. The official reference is the WSL containers documentation from Microsoft.

Is your wslc setup ready? A quick checklist

Work through these on your own machine before you move a project over. Each one is a check you can run in under a minute.

  1. wsl --version and wslc version report the same release.
  2. wslc system session list shows a session after your first wslc pull.
  3. wslc ps shows the port bound to the address you intended, 127.0.0.1 or 0.0.0.0.
  4. wslc exec app getent hosts db resolves containers on your user network.
  5. Every prune in your scripts carries -f, and volume prunes carry -a only where you mean it.
  6. wslc info points at the settings.yaml you edited.

If item 4 fails, check that both containers were started with --network appnet, since the default bridge gave us no name resolution at all. If anything in your workflow still needs compose, keep that project on Docker for now and recheck wslc --help after each WSL release.

Keep reading

Claude Code Cheat Sheet: Commands, Shortcuts and Flags (PDF) AI Claude Code Cheat Sheet: Commands, Shortcuts and Flags (PDF) OpenAI Codex CLI Cheat Sheet: Shortcuts, Commands, Flags (PDF) AI OpenAI Codex CLI Cheat Sheet: Shortcuts, Commands, Flags (PDF) Best UI Applications for Managing Docker Containers Containers Best UI Applications for Managing Docker Containers CapRover Docker Install on Debian 13: Deploy Your First App Containers CapRover Docker Install on Debian 13: Deploy Your First App APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Cheat Sheets APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Install Kubernetes on Ubuntu 24.04 Using K3s Containers Install Kubernetes on Ubuntu 24.04 Using K3s

Leave a Comment

Press ESC to close