Deploy to local Docker Desktop with a ComposeHost target
Audience: operators deploying a Stella Ops release onto the same Windows + Docker Desktop host that runs the enrolled agent-core container.
Use this path when the workload belongs on the local Docker Desktop engine. The deployment does not use SSH, WinRM, or an exposed Docker TCP daemon. agent-core runs docker compose through its mounted /var/run/docker.sock.
Prerequisites
- Docker Desktop is running Linux containers.
- The Stella Ops stack is running and
agent-corewas enrolled with theDockerandComposecapabilities. See Agent host bootstrap. - You have an existing Stella Ops environment and a release whose target environment is that environment.
- You have a bearer token with
orch:read,orch:operate, and the release permissions needed to deploy. Set$token,$tenant, and$baseUrlin the PowerShell session; do not paste tokens into files or shell history. - The release already satisfies its evidence, approval, policy, and digest gates. Creating a target does not bypass them.
Run the commands from the repository root in PowerShell 7. They use the public Release Orchestrator API through the gateway.
$baseUrl = "https://stella-ops.local"
$tenant = "internal"
$headers = @{
Authorization = "Bearer $token"
"X-Stella-Tenant" = $tenant
}
1. Confirm the local agent
List active agents and select the agent running on this Docker Desktop host:
$agents = Invoke-RestMethod `
-Uri "$baseUrl/api/v1/release-orchestrator/agents?status=Active" `
-Headers $headers `
-SkipCertificateCheck
$agents | Select-Object id, name, status, capabilities, lastHeartbeatAt
The selected row must be Active, have a recent lastHeartbeatAt, and include both Docker and Compose in capabilities.
The repository Compose profile gives agent-core local-engine access by mounting the configured Docker socket at /var/run/docker.sock; its normal endpoint is unix:///var/run/docker.sock. Do not expose Docker Desktop on tcp://localhost:2375 for this workflow.
$agentId = "<active-local-agent-guid>"
If no suitable agent exists, stop and enroll or repair it. Do not substitute an agent on another host: a local ComposeHost deployment always lands on the Docker engine visible to the assigned agent.
2. Resolve the environment
$environmentName = "partner-local"
$environment = Invoke-RestMethod `
-Uri "$baseUrl/api/v1/release-orchestrator/environments/by-name/$environmentName" `
-Headers $headers `
-SkipCertificateCheck
$environmentId = $environment.id
3. Create the local ComposeHost target
The absence of sshUsername is the local-execution switch. Do not add sshUsername, sshPort, or sshPrivateKeySecretRef.
$targetName = "docker-desktop-local"
$connectionConfig = @{
type = "compose_host"
host = "host.docker.internal"
port = 2375
useRemoteDockerEndpoint = $false
useTls = $false
composeProjectPath = "/var/lib/stella-agent/deployments"
composeFile = "docker-compose.yml"
}
$createBody = @{
name = $targetName
displayName = "Local Docker Desktop"
type = "ComposeHost"
agentId = $agentId
connectionConfig = $connectionConfig
} | ConvertTo-Json -Depth 5
$target = Invoke-RestMethod `
-Method Post `
-Uri "$baseUrl/api/v1/release-orchestrator/environments/$environmentId/targets" `
-Headers $headers `
-ContentType "application/json" `
-Body $createBody `
-SkipCertificateCheck
$targetId = $target.id
host is retained as target/probe metadata. It does not become DOCKER_HOST while useRemoteDockerEndpoint=false. The orchestrator injects DOCKER_HOST only for an SSH-mode ComposeHost (ssh://...) or an explicitly remote TCP target, so this target uses the agent’s configured local Docker socket. composeProjectPath is a Linux path visible inside agent-core, not a Windows C:\... path.
If the target already exists, read it by natural key and update it instead of creating a duplicate:
$target = Invoke-RestMethod `
-Uri "$baseUrl/api/v1/release-orchestrator/environments/$environmentId/targets/by-name/$targetName" `
-Headers $headers `
-SkipCertificateCheck
$targetId = $target.id
$updateBody = @{
displayName = "Local Docker Desktop"
connectionConfig = $connectionConfig
agentId = $agentId
} | ConvertTo-Json -Depth 5
Invoke-RestMethod `
-Method Put `
-Uri "$baseUrl/api/v1/release-orchestrator/targets/$targetId" `
-Headers $headers `
-ContentType "application/json" `
-Body $updateBody `
-SkipCertificateCheck
4. Check target readiness
$check = Invoke-RestMethod `
-Method Post `
-Uri "$baseUrl/api/v1/release-orchestrator/targets/$targetId/health-check" `
-Headers $headers `
-SkipCertificateCheck
$check
Expected: success is true. For an agent-backed ComposeHost this check proves that the assigned agent is active, its heartbeat is fresh, and it advertises Docker + Compose. The deployment runtime preflight separately probes the local Docker daemon, Compose availability, work directory, image platform, disk, and configured registry mode.
5. Deploy the release
Confirm that the release targets $environmentId, then use the normal governed deploy action:
stella release show <release-id>
stella release deploy <release-id> --reason "Deploy to local Docker Desktop ComposeHost" --json
The deploy can still stop at evidence, approval, policy, digest, preflight, or access gates. Resolve the reported gate; do not weaken it merely to make the local path run.
For a component-level post-deploy probe, prefer an explicit healthProbe.http.url or healthProbe.host that is reachable from the Release Orchestrator container. The target’s host value is only the default probe host; it does not control which Docker engine executes the deployment.
6. Verify independently
Do not treat an accepted HTTP request or a healthy agent as proof that the workload landed. Wait for the deployment to reach succeeded, then independently query Docker Desktop:
$deploymentId = "<deployment-id-from-deploy-response>"
$deployment = Invoke-RestMethod `
-Uri "$baseUrl/api/v1/release-orchestrator/deployments/$deploymentId" `
-Headers $headers `
-SkipCertificateCheck
$deployment.status
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
Required evidence:
- deployment status is
succeeded; - the expected container names and digest-pinned images appear in
docker ps; - containers that declare a health check report
healthy; - the deployment target in Stella Ops is
$targetIdand the reported agent is$agentId.
Failure modes
Health check says the assigned agent is missing or stale
Verify agent-core enrollment and heartbeat first. Recreating the container must preserve the identity from devops/compose/env/agent-core.local.env; do not register a replacement identity just to clear a stale target assignment.
docker compose cannot connect to the daemon
Check Docker Desktop and the agent-core socket contract. The Compose service mounts ${AGENT_CORE_DOCKER_SOCKET:-/var/run/docker.sock} to /var/run/docker.sock and normally configures Agent__DockerEndpoint=unix:///var/run/docker.sock. Do not fix this by enabling unauthenticated TCP port 2375.
Deployment ran on the wrong engine
The assigned agent defines the local engine. Confirm the target’s agentId. Also confirm that sshUsername is absent; if it is present, the target is docker-over-SSH and intentionally remote.
Work-directory preflight fails
Use a Linux container path writable by agent-core, normally /var/lib/stella-agent/deployments. Do not supply a Windows filesystem path.
Application is running but post-deploy probe fails
Inspect the explicit healthProbe.* configuration and Docker networking. Deployment-engine success and application reachability are separate assertions; point the probe at an address reachable from the orchestrator rather than assuming its localhost is the Windows host.
Contract references
- ComposeHost model:
src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Environment/Models/TargetConnectionConfig.cs - Target routing and local/SSH
DOCKER_HOSTbehavior:src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Deployment/Executor/TargetExecutor.cs - Agent readiness check:
src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Services/AgentAwareTargetConnectionTester.cs - Docker socket mount:
devops/compose/docker-compose.stella-services.yml - Docker-gated behavioral proof:
src/ReleaseOrchestrator/__Tests/StellaOps.ReleaseOrchestrator.Integration.Tests/InProcessLocalComposeDeploymentE2ETests.cs
