AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Local Deployment

skill-evolplus-talos-local-deployment · by evolplus

How DevOps composes the local environment using Docker so QA-Exec can run end-to-end tests AND the operator can manually trial features in a browser. Probes available host ports instead of hardcoding, discovers project .env/template/env_file requirements without exposing secrets, never edits the project's compose file directly, and produces a deploy report with QA test-environment fields and huma…

No reviews yet
0 installs
34 views
0.0% view→install

Install

$ agentstack add skill-evolplus-talos-local-deployment

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-evolplus-talos-local-deployment)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Local Deployment? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Local Deployment

When to use

You are DevOps, dispatched against a task in status ready-for-deploy. Your job is to bring the local environment up — typically FE + BE + datastores composed via Docker — so two consumers can work against it:

  1. QA-Exec runs the test runner against base_url / api_base_url per the kit's QA-Exec Run Contract.
  2. The operator (PM, designer, eng lead) opens the running app in a browser to manually trial the feature before sign-off — confirming the feature looks and behaves right against intent, not just against test assertions.

The skill covers Docker-based composition, port discovery (probe-then-bind, never hardcode), and deploy-report population. For non-Docker stacks (rare in modern projects), consult docs/architecture.md for the project's documented run mode.

Project-scoped container discipline (safety rule)

The kit dispatches DevOps inside an operator's environment that almost always has unrelated containers running — the operator's personal Postgres, another project's Redis, a sibling repo's docker-compose stack. A careless docker compose down -v would nuke them all. DevOps's blast radius is scoped to the current project's containers only.

Project slug — single source of truth

Determine the project slug (the Compose project name) once at deploy start; thread it through every subsequent docker command. Lookup priority:

  1. COMPOSE_PROJECT_NAME environment variable if set (most explicit; respects the operator's intent).
  2. SRS header's project name field if Phase 0 read SRS successfully — sanitize: lowercase(name).replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''). Example: "Stats Overflow"stats-overflow.
  3. Working-directory basename as a fallback when neither of the above is available. Same sanitization.

Record the chosen slug at the top of the deploy report (## Test Environment block adds project_slug: field) so QA-Exec and the operator can verify.

Every command takes the slug

Apply the slug to every docker invocation:

slug="stats-overflow"  # from lookup above

# Compose: pass -p explicitly
docker compose -p "$slug" -f docker-compose.yml -f docker-compose.override.yml up -d --wait
docker compose -p "$slug" down                          # tear-down
docker compose -p "$slug" logs --tail=200 api           # inspection

# Plain docker ps: filter by Compose-project label
docker ps --filter "label=com.docker.compose.project=$slug"
docker ps --filter "name=^${slug}-"                     # if not using Compose labels

# Container operations: name MUST start with the slug
container_name="${slug}-api-1"
docker inspect "$container_name"
docker logs "$container_name"

Out-of-scope reads — explicitly allowed (for conflict detection only)

Conflict detection legitimately requires reading what ELSE is running on the host. The following READ operations are permitted on out-of-scope containers / system state:

| Operation | Why allowed | |---|---| | docker ps (no filter) | Conflict detection — what ports are in use, what's running | | docker ps -a | Same — including stopped containers | | docker inspect | Read-only inspection for conflict resolution | | docker logs | Read-only diagnostics if a port owner is identified | | docker port | Read which ports a container is publishing | | docker stats --no-stream | Resource visibility | | docker version / docker info | System info | | docker network ls / docker network inspect | Network state visibility (read-only) | | docker volume ls / docker volume inspect | Volume state visibility (read-only) |

These are HOW DevOps probes ports + detects conflicts. They never mutate state.

Out-of-scope mutations — explicitly forbidden

DevOps MUST NOT run any of the following:

| Operation | Why forbidden | |---|---| | docker stop where ` doesn't start with | Could stop the operator's personal services | | docker rm where doesn't start with | Same | | docker kill where doesn't start with | Same | | docker restart where doesn't start with | Same | | docker pause / unpause outside slug | Same | | docker compose -p ANY mutation | Operates on another project entirely | | docker compose down without -p | Defaults to cwd basename → might match wrong project if cwd is non-standard | | docker system prune | Nukes ALL unused containers / networks / volumes across all projects | | docker volume prune | Nukes unused volumes globally | | docker network prune | Nukes unused networks globally | | docker container prune | Nukes all stopped containers globally | | docker image prune | Nukes unused images globally — affects other projects' rebuild times | | docker rm -f $(docker ps -q) (and variants with $(docker ps ...)) | Force-removes ALL running containers | | docker stop $(docker ps -q) | Stops ALL running containers | | docker volume rm outside slug | Could destroy operator's data | | docker network rm outside slug | Could break other projects' networking | | docker image rm ` | Affects every project that uses the image |

The docker-scope-guard.cjs hook enforces these at runtime — DevOps will be refused before the command runs.

Conflict resolution — non-mutating recourse

When an out-of-scope container holds a port the kit project needs:

  1. Probe and report — DevOps notes "port 5432 occupied by mysite-postgres-1 (not in scope)" in the deploy report.
  2. Pick a different port — port-probe algorithm finds a free one (see Step 4 above).
  3. Surface to the operator if no port is available — write the conflict into docs/open-issues.md with category local-port-exhaustion, halt the dispatch, return NEEDS_CONTEXT asking the operator to manually free a port (their decision; they own those containers).

NEVER auto-resolve by stopping the other container.

Inputs and outputs

  • Inputs: the project's existing docker-compose.yml (or equivalent); project env templates and compose env_file: references; docs/architecture.md C2 Containers section (to know what services to expect); the task ID; SRS §3.4 Technical Constraints if it pins ports.
  • Outputs: a running local environment (containers up + health-checked); docs/deploy-reports/.md with the standard Test Environment block + a new ## Human Trial URLs section both QA and the operator consume.

Procedure

Step 1 — Verify Docker

Run two checks:

docker info > /dev/null 2>&1 || { echo "docker daemon not running"; exit 1; }
docker compose version > /dev/null 2>&1 || { echo "docker compose v2 not available"; exit 1; }

If either fails, halt with NEEDS_CONTEXT:

Status: NEEDS_CONTEXT
Reason: Docker prerequisite missing.
Question: docker daemon not running OR docker compose v2 not installed. The local-deployment skill requires both.
Suggested resolution: Start Docker Desktop / colima / podman; verify with `docker info` then `docker compose version`.

Do NOT attempt to install Docker yourself — that's an operator decision.

Step 1.5 — Detect host architecture + compute target platform

Docker images are architecture-specific. The kit's most common failure mode here is silently shipping amd64 images on Apple Silicon (arm64 Darwin) — they technically run via Rosetta emulation but at 30–60% performance penalty, occasional binary incompatibilities (e.g., native node-gyp modules), slow startup, and confusing error messages. Detect host arch BEFORE bringing the env up; match images deliberately.

host_os=$(uname -s)       # Darwin / Linux
host_arch=$(uname -m)     # x86_64 / arm64 / aarch64

# Normalize to Docker platform notation
case "$host_arch" in
  x86_64|amd64)         target_platform="linux/amd64" ;;
  arm64|aarch64)        target_platform="linux/arm64" ;;
  *)                    echo "Unknown arch: $host_arch"; exit 1 ;;
esac

echo "Host: $host_os $host_arch → target_platform=$target_platform"

Host → target-platform mapping:

| Host | uname -m | targetplatform | Notes | |---|---|---|---| | Apple Silicon Mac (M1/M2/M3/M4) | arm64 | linux/arm64 | Native; preferred | | Intel Mac | x86_64 | linux/amd64 | Native | | Linux x8664 (most servers / cloud VMs) | x86_64 | linux/amd64 | Native | | Linux ARM64 (AWS Graviton, Ampere, RPi) | aarch64 | linux/arm64 | Native | | Windows (WSL2) | per WSL distro | per arch | Same rules apply inside WSL |

Inspect compose file for explicit platform pins. Many projects pin services to linux/amd64 for historical reasons (image vendor only published amd64, team standardized on Intel laptops, etc.). Walk the compose file's services.*.platform field:

# Pseudo: extract platform pins
yq '.services[] | select(.platform != null) | .platform' docker-compose.yml

For each pinned service, classify against the host:

| Compose platform | Host target | Action | |---|---|---| | Not pinned | any | Pass --platform=$target_platform to docker compose; Docker selects native image if available, falls back to multi-arch manifest | | linux/amd64 | linux/amd64 host | Native; proceed | | linux/amd64 | linux/arm64 host (Apple Silicon!) | Emulation warning — image runs via Rosetta/QEMU at ~50% performance. Flag in deploy report; consider asking SA for a multi-arch base image migration. | | linux/arm64 | linux/arm64 host | Native; proceed | | linux/arm64 | linux/amd64 host | Emulation warning (rare; usually means compose was authored on Apple Silicon and never re-tested on Intel CI) | | Multi-arch image (no platform pin, image manifest lists both) | any | Docker picks native automatically; preferred |

Apply the platform to every relevant command. Pass --platform=$target_platform to:

  • docker compose -p up -d --wait → if compose-file platform: fields aren't explicit, this overrides; if they ARE explicit, this gets ignored per-service (compose-file wins).
  • docker build --platform=$target_platform -t . (and buildx invocations) — when building from local Dockerfiles.
  • docker run --platform=$target_platform — when running standalone containers.

For projects shipping amd64-only images to mixed-arch teams: the right answer is a multi-arch base image (build with docker buildx build --platform=linux/amd64,linux/arm64). Surface this as an open-issue with category multi-arch-base-needed and let SA pick up the migration.

Step 2 — Discover the project's compose definition

Look for, in order:

  1. docker-compose.yml at project root (most common).
  2. compose.yml (Compose v2 convention).
  3. infra/docker-compose.yml, deploy/docker-compose.yml (per kit's "project-owned reusable infra" pattern).
  4. A multi-file split: docker-compose.yml + docker-compose.local.yml (project's local overlay).

If NONE exists, halt:

Status: NEEDS_CONTEXT
Reason: No compose file found at project root or under infra/ / deploy/.
Question: The kit cannot author a compose file inline (per the DevOps template's "project-owned reusable infra" rule). Either:
  [a] The project doesn't have local-deploy machinery yet — needs an architecture decision (escalate to SA / TL via open-issue with category `infra-decision-pending`).
  [b] The compose file lives elsewhere — name the path.

Step 2.5 — Discover environment files and validate env readiness

Do this before port probing or docker compose up. Many local deploy failures are really env-loading failures: Compose was run from the wrong directory, the root .env was not loaded, a service-level env_file: was missing, or QA ran with defaults that differ from the operator's intended local setup.

Classify env files:

  • Operator-owned secret env files: .env, .env.local, .env.development, .env.test, .env., and any file named by compose env_file: unless it is an allowlisted template. Detect existence and usage, but never read or print values.
  • Safe templates: .env.example, .env.template, .env.sample, including service-specific variants. These may be read to collect required key names and comments.

Procedure:

  1. Inspect the selected compose files and project run docs for:
  • env_file: entries per service;
  • ${VAR} / ${VAR:-default} / ${VAR:?required} interpolation placeholders;
  • documented local env-file order, for example .env then .env.local.
  1. Read safe templates only. Extract key names, defaults, and comments; do not treat template placeholder values as deploy secrets.
  2. Detect whether operator-owned env files exist and whether every compose-referenced env_file: path exists. Do not copy env files into the worktree and do not create missing files.
  3. Build a compose_env_args list for every later Compose command:
  • run from the project root or pass --project-directory so root .env participates in interpolation;
  • include explicit --env-file only when the project docs/scripts/compose setup declare that file order;
  • preserve service-level env_file: entries in compose instead of copying them into the generated override.
  1. Validate the selected compose/env files before port probing:

``bash # Use the same -p, -f, --project-directory, and --env-file arguments that the final deploy will use, # but do not include the generated override yet because Step 5 has not created it. docker compose --project-directory "$project_root" "${compose_env_args[@]}" -p "$slug" -f docker-compose.yml config --quiet ``

Use config --quiet, not full docker compose config, because full config output can print resolved secret values.

  1. If validation fails because a required env var or env file is missing, halt with NEEDS_CONTEXT. Ask the operator to create/update the project-local env file manually. Do not write .env yourself.
  2. If privacy hooks block key-only inspection of operator-owned env files, record key_status: not inspected (privacy guard) in the deploy report. Do not bypass privacy just to count keys.

The deploy report must include an env summary with file names and statuses only:

### Env File Awareness

| Item | Status | Notes |
|---|---|---|
| `.env` | present | operator-owned; values not read or printed |
| `.env.local` | absent | not declared by project docs |
| `api/.env` via compose `env_file` | present | service-level env file |
| `.env.example` | read | 18 documented keys |

- compose_config_quiet: pass
- missing_required_env: none
- secret_values_redacted: true

Step 3 — Determine preferred port ranges

Consult, in priority order:

  1. SRS §3.4 Technical Constraints if it names port preferences for the project.
  2. .claude/skills/solution-defaults/references/defaults-table.md if it lists project port defaults.
  3. The compose file's published ports (ports: lists; treat as preferences, not mandates — they may conflict on the host).
  4. Kit defaults (table below) when none of the above apply.

| Service type | Preferred range | Container-internal default | |---|---|---| | Web FE (Next.js / Vite / CRA) | 3000–3010 | 3000 | | Backend HTTP API | 4000–4010 OR 8080–8090 | varies | | Admin / internal | 4100–4110 | varies | | WebSocket gateway | 4200–4210 | varies | | Postgres | 5432–5442 | 5432 | | MySQL | 3306–3316 | 3306 | | Redis | 6379–6389 | 6379 | | Elasticsearch | 9200–9210 | 9200 | | Kafka broker | 9092–9102 | 9092 | | RabbitMQ | 5672–5682 | 5672 | | MinIO / S3-compat | 9000–9010 + 9100–9110 (console) | 9000 / 9001 |

The preferred range gives predictability for the operator (no random ports each run) while leaving headroom when the defaults are occupied.

Step 4 — Probe ports

For each service, walk its preferred range and find the first host port not in LISTEN state:

probe_port() {
  local start=$1 end=$2
  for port in $(seq "$start" "$end"); do
    if ! lsof -iTCP:"$port" -sTCP:LISTEN -t > /dev/null 2>&1; then
      echo "$port"; return 0
    fi
  done
  retur

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [evolplus](https://github.com/evolplus)
- **Source:** [evolplus/talos](https://github.com/evolplus/talos)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.