wslc-compose
docker-compose style orchestration for WSL containers (wslc).
Microsoft's new wslc CLI (the WSL container public preview, June 2026) manages single
containers much like docker run, but it has no Compose support yet — the WSL team
tracks the feature request in microsoft/WSL#40948
and a Docker-compatible API endpoint in microsoft/WSL#40976.
wslc-compose fills that gap today: point it at the docker-compose.yml /
compose.yaml you already use with Docker or Podman, and it drives wslc for you —
networks with DNS, named volumes, bind mounts, dependency ordering, project-scoped
naming, config-drift detection and scaling included.
Install in one line
pipx install "wslc-compose==0.*" # or: uv tool install "wslc-compose==0.*" / pip install --user "wslc-compose==0.*"
Published on PyPI — more install options.
Table of contents
- How it works
- Requirements
- Installation
- Quick start
- Migrating an existing wslc setup
- Command reference
- Compose file support
- Secrets and configs
- Variable interpolation
- Naming conventions and labels
- How
updecides what to do - Networking
- Volumes and path translation
- Known wslc preview limitations
- Troubleshooting
- Examples
- Project layout
- Development
- License
How it works
wslc-compose is a thin, dependency-light Python CLI (only PyYAML) that:
- Loads your compose file (
compose.yaml,compose.yml,docker-compose.ymlordocker-compose.yaml, searched in the current directory and its parents), applies.env+ environment variable interpolation, and normalizes everything into an internal model. - Plans the work: which networks/volumes must exist, which images must be built or
pulled, in which order services must start (
depends_ontopological sort), and which existing containers are stale (config-hash comparison). - Executes plain
wslccommands (run,build,network create,volume create,stop,remove,logs, ...). There is no daemon, no state file, no magic — run any step with--dry-runto see the exactwslcinvocations and paste them in a terminal yourself if you want.
Because state lives entirely in wslc (via container labels), wslc-compose and the raw
wslc CLI can be mixed freely.
Two executables are installed:
| Command | Purpose |
|---|---|
wslc-compose |
the standalone compose CLI |
wslc |
a wrapper giving the docker/podman UX: wslc compose up -d; any other subcommand is forwarded verbatim to the real wslc CLI (wslc images, wslc attach, ...). A recursion guard makes sure the wrapper never invokes itself. |
Requirements
- Windows 11 with the WSL container preview installed —
wslcmust work in a terminal. See the official documentation. - Python ≥ 3.9, inside a WSL distro or on Windows. (No pip/venv on your distro? The one-liner installer handles that.)
Installation
From PyPI (recommended)
wslc-compose is published on PyPI. Pin the
0.x series so a future breaking 1.0 never lands by surprise:
pipx install "wslc-compose==0.*"
# or
uv tool install "wslc-compose==0.*"
# or
pip install --user "wslc-compose==0.*"
(drop the ==0.* to always get the latest release, or use ==0.3.1 to pin exactly.)
Upgrade later with pipx upgrade wslc-compose (or uv tool upgrade wslc-compose /
pip install --user -U "wslc-compose==0.*").
One-liner (no pip, venv or pipx on your distro)
If your WSL distro has no Python package manager, this installer bootstraps a standalone
uv. It installs from the GitHub repo by default; set
WSLC_COMPOSE_SOURCE to get the PyPI release instead:
curl -fsSL https://raw.githubusercontent.com/bacarndiaye/wslc-compose/main/install.sh \
| WSLC_COMPOSE_SOURCE='wslc-compose==0.*' sh
On Windows PowerShell:
irm https://raw.githubusercontent.com/bacarndiaye/wslc-compose/main/install.ps1 | iex
Commands are installed into ~/.local/bin (make sure it is on your PATH).
From Git (unreleased changes)
pipx install git+https://github.com/bacarndiaye/wslc-compose
# or
uv tool install --from git+https://github.com/bacarndiaye/wslc-compose wslc-compose
# or
pip install --user git+https://github.com/bacarndiaye/wslc-compose
From a local checkout
git clone https://github.com/bacarndiaye/wslc-compose
cd wslc-compose
WSLC_COMPOSE_SOURCE=$PWD sh install.sh # or: pip install -e .
On Windows PowerShell:
git clone https://github.com/bacarndiaye/wslc-compose
cd wslc-compose
$env:WSLC_COMPOSE_SOURCE = $PWD; .\install.ps1 # or: pip install -e .
Locating the wslc binary
wslc-compose finds the wslc CLI automatically, in this order:
$WSLC_COMPOSE_BIN(explicit override)wslc.exe/wslconPATH(Windows interop makeswslc.exevisible inside WSL)C:\Program Files\WSL\wslc.exe(default install location)
Quick start
$ cd my-project # contains compose.yaml
$ wslc compose up -d
Network my-project_default created
Volume my-project_data created
Creating my-project-db-1 ...
Creating my-project-web-1 ...
$ wslc compose ps
NAME SERVICE IMAGE STATUS PORTS
my-project-db-1 db postgres:16 running
my-project-web-1 web nginx:alpine running 0.0.0.0:8080->80/tcp
$ wslc compose logs -f web
$ wslc compose exec web sh
$ wslc compose down -v
$ wslc images # ← not a compose command: forwarded to wslc.exe verbatim
wslc-compose up -d, wslc-compose ps, ... work identically if you prefer the
standalone command.
Migrating an existing wslc setup
Already running containers with raw wslc run commands or a home-grown script?
docs/MIGRATION.md walks through the whole move, end to end:
inventorying what runs, reusing already-built images (retag instead of rebuild),
retiring the old containers without breaking published ports, verifying, and rolling
back — plus a recovery cheat sheet for the preview's sharp edges (per-elevation
sessions, mount budget, wedged sessions).
Command reference
Global options
Apply to every subcommand and go before it: wslc-compose -f other.yml up -d.
| Option | Description |
|---|---|
-f, --file FILE |
compose file to use; repeat to overlay several (later files win, like docker compose -f a.yml -f b.yml). Paths may also be joined with : (; on Windows) or set via COMPOSE_FILE. Default: auto-detect in . and parent directories |
-p, --project-name NAME |
project name (default: name: key, else directory name) |
--env-file FILE |
alternate .env file for interpolation |
--profile NAME |
enable a compose profile (repeatable) |
--dry-run |
print every wslc command instead of executing it |
--version |
show the wslc-compose version |
up [SERVICE...]
Create networks and volumes, build missing images, then create/start containers in
depends_on order. Idempotent: up-to-date running containers are left alone.
| Option | Description |
|---|---|
-d, --detach |
do not attach to logs after starting |
--build |
rebuild images of services that have a build: section |
--force-recreate |
recreate containers even if their configuration is unchanged |
--scale SERVICE=N |
override the number of replicas (repeatable) |
-t, --timeout SEC |
stop timeout when recreating (default 10) |
Without -d, logs of the started services are followed after startup; Ctrl+C
detaches without stopping the containers.
down
Stop and remove all of the project's containers, then remove its non-external networks.
| Option | Description |
|---|---|
-v, --volumes |
also remove the project's non-external named volumes |
-t, --timeout SEC |
stop timeout (default 10) |
ps [SERVICE...]
List the project's containers (name, service, image, status, ports).
-q prints container IDs only.
logs [SERVICE...]
Show container logs, multiplexed and prefixed per container
(my-project-web-1 | ...).
| Option | Description |
|---|---|
-f, --follow |
follow log output |
-n, --tail N |
show only the last N lines |
-t, --timestamps |
show timestamps |
exec SERVICE CMD [ARG...]
Run a command in a running container of SERVICE.
| Option | Description |
|---|---|
--index N |
pick the Nth replica (default 1) |
-u, --user USER |
run as user |
-w, --workdir DIR |
working directory |
-e, --env K=V |
extra environment variables (repeatable) |
-T, --no-tty |
disable TTY allocation (for scripts/pipes) |
A TTY is allocated automatically when stdin is a terminal.
start / stop / restart [SERVICE...]
Lifecycle of existing project containers, without recreating them.
restart is emulated as stop + start (wslc has no native restart).
pull [SERVICE...]
Pull the images of services that have an image: key.
build [--no-cache] [SERVICE...]
Build every selected service that has a build: section, tagging the result
<project>-<service> (unless image: names it explicitly).
config
Print the fully resolved configuration (after interpolation, normalization, name prefixing) as YAML — useful to debug what wslc-compose actually sees.
version
Show the wslc-compose and wslc versions.
Compose file support
| Compose key | Mapping to wslc |
|---|---|
image |
wslc run <image> / wslc pull |
build (context, dockerfile, args, target, pull) |
wslc build -t <project>-<service> |
command, entrypoint (string or list) |
trailing args / --entrypoint |
container_name |
--name (disables scaling for that service) |
environment (list & map), env_file |
-e, --env-file |
ports (short & long syntax, ip:host:container, /udp, ranges 8000-8005) |
-p |
volumes — named volumes |
wslc volume create + -v name:/path |
volumes — bind mounts (./rel, /abs, ~, E:\win\path), :ro |
-v with path translation |
tmpfs (top-level list or type: tmpfs) |
--tmpfs |
Multiple networks, per-endpoint aliases / ipv4_address, external: true, custom name: |
Native create, network connect, start; one-network services use run |
Network driver, driver_opts, labels, internal, IPv4 IPAM subnet / gateway / ip_range |
network create --driver --opt --internal --subnet --gateway --ip-range |
depends_on (list & map) |
topological start/creation order (condition: is treated as service_started, see limitations) |
healthcheck (test, interval, timeout, start_period, retries, disable) |
--health-cmd, --health-interval, --health-timeout, --health-start-period, --health-retries, --no-healthcheck — the status shows in ps as (healthy) / (unhealthy) |
deploy.replicas |
number of containers (see also --scale) |
deploy.resources.limits.cpus / .memory, cpus, mem_limit |
--cpus, -m |
deploy.resources.reservations.devices (gpu), gpus |
--gpus |
shm_size, ulimits, stop_signal |
--shm-size, --ulimit, --stop-signal |
hostname, domainname, dns, dns_search, dns_opt |
-h, --domainname, --dns* |
user, working_dir |
-u, -w |
labels (list & map) |
-l (merged with the tracking labels below) |
profiles |
service skipped unless its profile is enabled or it is named explicitly |
stdin_open, tty |
-i, -t |
secrets / configs (file: source; short & long service syntax) |
read-only file mount at /run/secrets/<name> (secrets) or /<name> (configs), or target — see Secrets and configs |
name (top level) |
default project name |
include (string, list, or path: mapping) |
other compose files merged into the project; relative paths inside an included file resolve against its directory |
Keys that wslc cannot honor yet are accepted and reported as a warning instead of
failing, so your existing files keep working: restart, privileged,
cap_add/cap_drop, devices, sysctls, init,
pid, ipc, read_only, security_opt, logging.
Rejected with an explicit error (no silent surprise): environment:/external: secrets and
configs, undefined secret/config references, build.secrets, duplicate service/network/volume
names across included files, circular or missing includes, anonymous volumes
(- /data without a source), references to undeclared networks/volumes, circular
depends_on, scaling a service that sets container_name.
Secrets and configs
File-backed secrets and configs use the standard Compose syntax. Each one is mounted
read-only, and only into the services that request it. Short syntax mounts a secret at
/run/secrets/<name> (a config at /<name>):
services:
restore:
image: mcr.microsoft.com/dotnet/sdk:10.0
secrets:
- nuget_config # -> /run/secrets/nuget_config
- source: feed_token
target: /root/.nuget/feed.token # absolute path, or a bare file name
secrets:
nuget_config:
file: C:/Users/me/.nuget/private.NuGet.Config
feed_token:
file: ./feed.token
Keep secret files out of source control and restrict their host permissions. Contents are
never copied into the model, labels or command line — only the host path is passed to
wslc -v.
Boundaries:
- Only
file:sources are supported;environment:,content:andexternal:fail with a clear error. uid,gidandmodeare accepted with a warning: wslc bind mounts cannot enforce them.build.secretsfails clearly —wslc buildhas no--secretoption. Do not replace it withbuild.args; run the credentialed step in a runtime container instead.- Every secret/config is a bind mount and counts toward the wslc session mount limit.
Multiple compose files
wslc-compose -f compose.yaml -f compose.override.yaml up -d
COMPOSE_FILE=compose.yaml:prod.yaml wslc-compose config
Files are overlaid in order: mappings are merged, ports/expose/dns/env_file/... are
appended, environment/labels merge per key, volumes merge per container path, and
scalars such as image or command are replaced. Relative paths resolve against the
first file's directory. A file may omit services (e.g. only volumes: or include:)
as long as the merged result has at least one service.
Variable interpolation
Identical to docker compose:
| Syntax | Behavior |
|---|---|
$VAR, ${VAR} |
value, empty if unset |
${VAR:-default} |
default if unset or empty |
${VAR-default} |
default only if unset |
${VAR:?message} / ${VAR?message} |
abort with message if missing |
${VAR:+alt} / ${VAR+alt} |
alt if set |
$$ |
literal $ (e.g. $$(cmd) reaches the container shell as $(cmd)) |
Precedence: process environment > .env file (in the project directory, or
--env-file). Shell constructs like $(date) are left untouched.
Naming conventions and labels
Everything is scoped by project so multiple projects coexist cleanly:
| Object | Name |
|---|---|
| container | <project>-<service>-<n> (or container_name:) |
| network | <project>_<network-key> (external networks keep their name) |
| volume | <project>_<volume-key> (external volumes keep their name) |
| built image | <project>-<service> (unless image: is set) |
wslc-compose records Compose metadata as WSLc labels. New resources use the
com.wslc-compose.* namespace, preserving the container labels used by earlier
versions:
com.wslc-compose.project project name
com.wslc-compose.version wslc-compose version that created it
com.wslc-compose.project.working_dir absolute project directory
com.wslc-compose.project.config_files JSON array of absolute Compose file paths
com.wslc-compose.project.environment_file absolute --env-file path, when supplied
com.wslc-compose.config-hash resolved service or resource configuration hash
com.wslc-compose.service container service name
com.wslc-compose.container-number container replica index (1..N)
com.wslc-compose.network Compose network key
com.wslc-compose.volume Compose volume key
Containers receive the project labels plus service and container-number.
Internally-created networks and named volumes receive the project labels plus their
respective network or volume key. This lets other WSLc tools identify all of the
resources belonging to a Compose project.
Inspect labels with the native WSLc commands:
wslc inspect <container-name>
wslc network inspect <network-name>
wslc volume inspect <volume-name>
Earlier versions applied only project, service, container-number, and
config-hash to containers. They did not label networks or volumes. Container
discovery still uses the existing com.wslc-compose.project label. Networks and
volumes continue to be managed by their resolved Compose names, so unlabelled
resources from earlier versions remain usable and removable.
Existing networks and volumes are never relabelled: up reuses them unchanged.
Likewise, resources declared with external: true are neither labelled nor adopted.
New internal resources receive the additional metadata labels. Recreating a named
volume removes its data when using down -v.
How up decides what to do
For every desired container, up compares the stored config-hash label against the
hash of the freshly resolved service configuration:
| Existing container | Action |
|---|---|
| running, hash matches | "is up-to-date" — untouched |
| stopped, hash matches | started |
hash differs, or --force-recreate |
stopped, removed, recreated |
| missing | created |
| replica index above the requested scale | removed |
Changing anything in the service definition (image, env, ports, mounts, ...) or in
interpolated variables therefore triggers a clean recreation of just the affected
services on the next up.
Networking
Host gateway and published ports
extra_hosts: ["host.docker.internal:host-gateway"] keeps the application's original
hostnames and published port numbers. WSLC exposes those ports on Windows using different
internal VM ports; adding the bridge IP to /etc/hosts alone does not reproduce Docker's
published-port hairpin routing. For projects requesting host-gateway, the frontend therefore
reconciles project-owned IPv4 DNAT/SNAT and forwarding chains inside the native WSLC session.
TCP/UDP publications with omitted host IP or 0.0.0.0 are supported; explicit loopback/interface
bindings are not widened. Only running containers owned by the same Compose project are targets.
The rules for bridge-network clients are refreshed on apply/start/restart/stop and removed by down. Compatible frontends
use the same owner/chain format and lock, preserve Docker's existing chains and DOCKER-USER
policy, and fail if an existing compatibility chain has a different owner. WSLC 3.0.3.0's
system session run and session iptables, iptables-restore and flock are required.
This handles published Compose service ports; it does not proxy arbitrary Windows-only host
services. /etc/hosts writing still uses root after startup until WSLC exposes native --add-host.
Compose networks
- Services without a
networks:key join the project'sdefaultnetwork (<project>_default), created on demand. - On user-defined wslc networks, containers resolve each other by container name
and by network alias —
wslc-composealways adds the service name as an alias, sodb:5432style URLs from your Docker compose files work unchanged. (Verified against the preview: alias and name DNS both resolve.) external: truenetworks are required to exist and are never created/removed.- Every declared network is attached. Multi-network services use create → connect
secondary networks → verify → start so the entrypoint sees every endpoint.
Single-network services keep the direct
runpath. - Each endpoint has independent aliases and an optional
ipv4_address, mapped to--iponcreate/runornetwork connect(validated with WSLc 3.0.3.0).upchecks every endpoint's aliases, requested allocation and live IP. Missing endpoints, alias/IP drift, a changed primary network or removal of a static IP trigger recreation. Unchanged running or stopped containers are reused without duplicate connections. Multiple replicas cannot share one static address. - Network declarations support
driver,driver_opts,labels,internaland the engine's IPv4 IPAM: one subnet per network, optionalgateway, and optionalip_rangefor dynamic allocations. Static IPs may be outside that dynamic pool, but must be usable addresses in the subnet and differ from the gateway. Existing networks are inspected before touching workloads; conflicting requested settings cause an error rather than silently reusing or rewriting the network. - WSLC 3.0.3.0 does not expose the complete Compose IPAM specification. Multiple
subnets in one network, IPv6, custom IPAM drivers/options,
aux_addresses, and unsupported endpoint controls such aspriority/gw_priorityare rejected explicitly. In the engine smoke, repeating--subnetretained only the last value, and an IPv6 declaration still reportedEnableIPv6=false. - Static endpoint IPs remain outside
com.wslc-compose.config-hash=v2:...; new IPAM creation details also remain outside its existing projection. Network resources and endpoint state are verified separately; no additional shared hash is used. - Failed multi-network creation removes only the partial container whose identity
and operation label were verified. It does not delete existing workloads or named
volumes. If ownership/inspect cannot be established, the container is retained for
inspection; a cleanup failure is reported. Created network/volume resources remain
available for retry or explicit
down. External and unverified networks are preserved. - See
examples/multi-network-ipam/compose.yamlfor a frontend/backend example with static addresses and a dynamic IP pool.
Volumes and path translation
- Named volumes are created on demand (
wslc volume create) and removed bydown -v(external ones never). - Bind mounts: relative paths are resolved against the compose file's directory,
~is expanded. wslc expects Windows host paths, so when running inside WSL, Linux paths are translated automatically withwslpath -w(/mnt/e/proj/html→E:\proj\html), with a manual/mnt/<drive>/...fallback. Paths already in Windows form (E:\...,\\server\...) are passed through as-is. - Paths living inside a distro filesystem translate to
\\wsl.localhost\<distro>\...; whether wslc can mount those depends on the preview build — prefer paths under a drive (/mnt/c,/mnt/e, ...). tmpfsmounts map to--tmpfs.
Known wslc preview limitations
wslc is a public preview; wslc-compose warns at load time rather than failing:
restart:policies — no wslc equivalent yet; ignored (arestartafter reboot is manual:wslc compose up -d).depends_on: condition: service_healthy/service_completed_successfully— not honored: the dependency is only waited on until it is started, not healthy (a warning is printed).healthcheckitself is supported and reported byps.- One IPv4 IPAM subnet per network, without IPv6/custom IPAM or auxiliary address reservations (see Networking). Multiple networks per container are supported.
- Session mount limit: the current preview caps mounted volumes at ~15 per WSL
session — error
Too many volumes have been mounted (limit: 15)/0x8007000e. This also affectswslc build(the build context is mounted). Microsoft says it will be fixed; meanwhilewsl --shutdownresets the session (⚠️ stops all WSL containers and distros). - Sessions are per Windows user and per elevation: an elevated terminal and a
normal one talk to two different wslc sessions with separate containers, images,
networks and volumes — invisible to each other, but competing for the same
127.0.0.1published ports. Pick one elevation and stick to it (details and recovery steps in docs/MIGRATION.md). - Sessions live in the Windows service and survive
wsl --shutdown. A wedged session (every wslc command hangs) can only be cleared by restarting the service from an admin PowerShell:Restart-Service WslService -Force. If that restart itself hangs (service stuck inStopPending), see the force-kill recovery in Troubleshooting. - Avoid
wslc system session terminatewhile containers run — in the current preview it can deadlock the whole wslc service (see above for the recovery). - Published ports bind the Windows loopback, not the distro's: test them from
the Windows side (browser,
powershell.exe Invoke-WebRequest), not with acurl localhostinside WSL. privileged,cap_add,devices,sysctls,logging— not configurable with wslc; ignored with a warning.
When wslc gains native Compose support (#40948) or a Docker Engine API endpoint (#40976), migrating away from this tool is trivial — your compose files never stopped being standard compose files.
Troubleshooting
wslc CLI not found — install the WSL container preview, or point
WSLC_COMPOSE_BIN at the binary (e.g. /mnt/c/Program Files/WSL/wslc.exe).
Too many volumes have been mounted (limit: 15) / error 0x8007000e — preview
session limit (see above). Restart WSL (wsl --shutdown from Windows) — this stops
all running WSL containers — then wslc compose up -d again.
A bind-mounted directory appears empty in the container — the path probably
reached wslc as a Linux path. Check the exact flags with --dry-run; sources should
show as E:\... style Windows paths. Paths inside the distro filesystem
(\\wsl.localhost\...) may not be mountable by the preview.
port is already allocated style errors — another container (maybe from a raw
wslc run) publishes the same host port; wslc list -a shows everything, not just
this project. If wslc list -a shows nothing on that port, the culprit likely
lives in the other elevation's session (see below).
Containers/images/networks suddenly "gone" — you probably switched between an
elevated and a normal terminal: each elevation has its own wslc session with its own
objects. wslc system session list shows them; go back to the terminal elevation
that created your containers.
Every wslc command hangs, even wslc list — the session's Windows-side relay is
deadlocked (known preview issue, e.g. after a system session terminate with running
containers). wsl --shutdown does not fix this — sessions survive it. From an
admin PowerShell: Restart-Service WslService -Force (older builds:
Restart-Service LxssManager -Force); this restarts all of WSL.
Restart-Service WslService -Force hangs / the service is stuck in StopPending —
the -Force restart asks the service to stop, but a wedged utility VM (a vmmem
process that refuses to die) keeps it from stopping, so it sits in StopPending
forever and every wsl.exe call stays suspended. Don't wait on it — kill the old
service process directly; because WslService is set to Automatic, Windows
restarts it cleanly with a fresh PID. From an admin PowerShell:
# 1) confirm the state and grab the stuck PID
Get-Service WslService | Select-Object Status # -> StopPending
$svcpid = (Get-CimInstance Win32_Service -Filter "Name='WslService'").ProcessId
# 2) force-kill the wedged service process; it auto-restarts (Automatic start)
taskkill /F /PID $svcpid
# 3) verify it came back, then start clean
Get-Service WslService | Select-Object Status # -> Running (new PID)
wsl.exe --shutdown # clear leftover sessions
A leftover vmmemwslc-* process may survive even wsl --shutdown (it is a protected
Hyper-V worker, not killable with taskkill, even as admin). It is inert and no
longer blocks the service — only a full Windows reboot clears it. Prefer killing the
stuck service PID (above) over rebooting.
Tip: run WSL commands over SSH from another machine, or from a Windows terminal that is not itself inside WSL — a
wsl --shutdown(or the kill above) then can't cut your own session out from under you.
ERROR_SHARING_VIOLATION on every session command, from every terminal —
wslc list/run fail with "the process cannot access the file because it is
being used by another process" everywhere, while wslc --version still answers:
the session store is locked machine-wide. We've hit this after many concurrent
wslc invocations (the preview tolerates only one at a time — serialize yours).
Recovery: wsl --shutdown; if it persists, the Restart-Service procedure above.
A published port works in the browser but not with curl localhost inside WSL —
expected: wslc publishes on the Windows loopback, which the distro's loopback
doesn't see. Test from the Windows side.
wslc-compose: command not found inside a script run from PowerShell —
bash script.sh from PowerShell starts a non-login shell without ~/.local/bin on
PATH. Add export PATH="$HOME/.local/bin:$PATH" at the top of the script.
What is it actually running? — add --dry-run to any command to see every
wslc invocation verbatim.
Examples
Both examples live in this repository and double as integration tests:
-
examples/demo— the full tour: nginx serving a bind-mounted page on port 8088, an alpine worker writing to a shared named volume, redis reachable through a network alias (cache),.envinterpolation,depends_onordering:cd examples/demo wslc compose up -d curl http://localhost:8088 wslc compose exec app cat /data/log.txt # "... (redis: up)" every 5 s wslc compose logs -f wslc compose down -v
-
examples/build— building an image from a local Dockerfile withbuild.args. -
examples/multiple-services— a rootcompose.yamlthat onlyincludesservice1.yamlandservice2.yaml(two nginx servers on 8089 / 8088).
Project layout
src/wslc_compose/
cli.py argument parsing + the up/down/ps/logs/... commands
shim.py the `wslc` wrapper command (compose → cli, rest → wslc.exe)
loader.py compose file discovery, parsing, normalization, validation
interpolation.py ${VAR...} substitution engine
model.py dataclasses: Project / Service / Network / Volume / mounts
flags.py pure functions building wslc argv from the model (unit-tested)
engine.py wslc binary discovery, subprocess layer, path translation,
container/network/volume queries
tests/ 25+ unit tests (no wslc needed — pure logic)
examples/ runnable demos (see above)
install.sh curl-able installer, bootstraps uv when pip/pipx are missing
Development
git clone https://github.com/bacarndiaye/wslc-compose
cd wslc-compose
pip install -e . pytest ruff # or the uv equivalent
pytest # unit tests, no wslc required
ruff check src tests
wslc-compose --dry-run -f examples/demo/compose.yaml up -d # inspect generated commands
CI runs the test matrix (Python 3.9 / 3.12 / 3.14) and ruff on every push and PR.
Issues and PRs are welcome — especially reports of wslc preview behavior changes, since the CLI surface is still evolving.
License
MIT © Bacar Ndiaye
Metadata
Release files for wslc-compose 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| wslc_compose-0.3.2.tar.gz | 84.9 kB | With provenance | Details for wslc_compose-0.3.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| wslc_compose-0.3.2-py3-none-any.whl | Python 3 | none | any | Details for wslc_compose-0.3.2-py3-none-any.whl |
Total release size: 133.7 kB
Release files / wslc_compose-0.3.2.tar.gz
| Download URL | wslc_compose-0.3.2.tar.gz |
|---|---|
| Size | 84.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ce79b729294de0dc1588994f5641d8aa9c06ecfa721a8addf1a883c9dfcdd122
|
|
BLAKE2b-256 checksum How to use checksums |
c7cd39c82c8e54328ec2b3e7902c51255e952ca09bd530d77ea38d3220d9753e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logRelease files / wslc_compose-0.3.2-py3-none-any.whl
| Download URL | wslc_compose-0.3.2-py3-none-any.whl |
|---|---|
| Size | 48.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3c87067680c683cd43f6ab798a8d7c446ecea46dda3a8a05834ec85b9cdc38fe
|
|
BLAKE2b-256 checksum How to use checksums |
b6990c779f22874be06c4383e0165cc33a8bf422801632fedc377a1b249d81ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logSecurity for wslc-compose 0.3.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for wslc_compose-0.3.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.