Skip to content
Back to skills

Cnt

BSecurity

How to write Dockerfile + compose.yaml — the develop/integrate service convention, multi-stage targets, dind helpers. Use when containerizing a project, writing docker compose services, or setting up integration tests.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
devopsgobashnodedockerkubernetesdebugging

Works with

  • vscode
  • cli

Security analysis

B84/100
  • criticalAccesses sensitive system or user directories
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill cnt --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cnt?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cnt
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-cnt/badge)](https://www.skillsdirectory.com/skills/majiayu000-cnt)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: sayt-cnt
description: >
  How to write Dockerfile + compose.yaml — the develop/integrate service
  convention, multi-stage targets, dind helpers.
  Use when containerizing a project, writing docker compose services, or setting up integration tests.
user-invocable: false
---

# launch / integrate — Docker Compose Containerization

`sayt launch` starts a containerized development environment. `sayt integrate` runs integration tests in containers. Both use Docker Compose with a specific service convention.

## How It Works

### `sayt launch`

1. Cleans up any leftover containers: `docker compose down -v --timeout 0 --remove-orphans`
2. Sets up Docker-in-Docker (dind) environment with a socat proxy
3. Runs: `docker compose run --build --service-ports develop`
4. Cleans up the socat container on exit

### `sayt integrate`

1. Cleans up: `docker compose down -v --timeout 0 --remove-orphans`
2. If `--no-cache`: builds without Docker layer cache first
3. Sets up dind environment
4. Runs: `docker compose up integrate --abort-on-container-failure --exit-code-from integrate --force-recreate --build --renew-anon-volumes --remove-orphans --attach-dependencies`
5. On success: cleans up containers
6. On failure: **leaves containers running** for inspection (run `docker compose logs` or `docker compose down -v` when done)

## The compose.yaml Convention

sayt expects two services in `compose.yaml`:

### `develop` service (for `sayt launch`)

The development service runs your app with hot reload, debugging, port mapping, etc.

```yaml
services:
  develop:
    command: ./gradlew dev -t          # Your dev command
    ports:
      - "8080:8080"                    # Expose ports to host
    build:
      network: host
      context: ../..                   # Monorepo root (or ".")
      dockerfile: services/myapp/Dockerfile
      secrets:
        - host.env
      target: debug                    # Dockerfile stage for dev
    volumes:
      - //var/run/docker.sock:/var/run/docker.sock
    entrypoint:
      - /monorepo/plugins/devserver/dind.sh
    secrets:
      - host.env
    network_mode: host
```

### `integrate` service (for `sayt integrate`)

The integration service runs your test suite in an isolated container.

```yaml
services:
  integrate:
    command: "true"                    # Overridden by Dockerfile CMD
    build:
      network: host
      context: ../..
      dockerfile: services/myapp/Dockerfile
      secrets:
        - host.env
      target: integrate               # Dockerfile stage for integration
```

### Secrets

```yaml
secrets:
  host.env:
    environment: HOST_ENV             # Injected by sayt's dind helper
```

The `HOST_ENV` secret contains Docker credentials, Kubernetes config, and dind connection info.

## Dockerfile Multi-Stage Pattern

sayt expects Dockerfiles with at least two targets:

### `debug` target (used by `develop`)

Contains the full development environment with source code, tools, and hot reload:

```dockerfile
FROM node:22 AS debug
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install
COPY . .
CMD ["pnpm", "dev"]
```

### `integrate` target (used by `integrate`)

Extends debug with integration test execution:

```dockerfile
FROM debug AS integrate
COPY tests/ tests/
CMD ["pnpm", "test:int"]
```

### Full Multi-Stage Example (JVM)

```dockerfile
# Base with tools
FROM eclipse-temurin:21 AS debug
WORKDIR /app
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts build.gradle.kts ./
COPY gradle/ gradle/
RUN ./gradlew dependencies
COPY src/main/ src/main/
COPY .vscode/ .vscode/
RUN ./gradlew assemble
COPY src/test/ src/test/

# Integration tests
FROM debug AS integrate
COPY src/it/ src/it/
CMD ["./gradlew", "integrationTest"]
```

### Full Multi-Stage Example (Node.js)

```dockerfile
FROM node:22 AS debug
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
CMD ["pnpm", "dev"]

FROM debug AS integrate
CMD ["pnpm", "test:int", "--run"]
```

## Docker-in-Docker (dind) Support

sayt provides dind helpers for scenarios where containers need to talk to Docker (e.g., testcontainers):

1. **socat proxy** — A socat container is started to proxy the Docker socket over TCP
2. **Environment variables** — `DOCKER_HOST`, `TESTCONTAINERS_HOST_OVERRIDE`, and `DOCKER_AUTH_CONFIG` are injected
3. **host.env secret** — All dind connection info is passed as a build secret

This enables testcontainers-based integration tests to create sibling containers.

## Host Networking

sayt services use `network_mode: host` by default. This means:
- Services can reach each other via `localhost`
- No port mapping conflicts
- Testcontainers can connect back to the host Docker daemon
- Works on Linux natively; on macOS, Docker Desktop provides host networking emulation

## Complete compose.yaml Example

```yaml
volumes:
  root-dot-docker-cache-mount: {}

services:
  develop:
    command: ./gradlew dev -t
    ports:
      - "8080:8080"
    build:
      network: host
      context: ../..
      dockerfile: services/tracker/Dockerfile
      secrets:
        - host.env
      target: debug
    volumes:
      - //var/run/docker.sock:/var/run/docker.sock
      - ${HOME:-~}/.skaffold/cache:/root/.skaffold/cache
    entrypoint:
      - /monorepo/plugins/devserver/dind.sh
    secrets:
      - host.env
    network_mode: host

  integrate:
    command: "true"
    build:
      network: host
      context: ../..
      dockerfile: services/tracker/Dockerfile
      secrets:
        - host.env
      target: integrate

secrets:
  host.env:
    environment: HOST_ENV
```

## Cleanup Behavior

- **Success**: `sayt integrate` automatically runs `docker compose down -v` to clean up
- **Failure**: Containers are **left running** so you can inspect logs:
  ```bash
  docker compose logs          # View all logs
  docker compose logs integrate # View integration service logs
  docker compose down -v       # Clean up manually when done
  ```

## Writing Good Compose Files for sayt

1. **Always define `develop` and `integrate`** — These are the services sayt expects
2. **Use `target:` in build** — `debug` for develop, `integrate` for integrate
3. **Set `context` to monorepo root** — Usually `../..` from a service directory
4. **Include the `host.env` secret** — Required for dind and credential forwarding
5. **Use `network_mode: host`** for develop — Simplifies networking
6. **Set `command: "true"` for integrate** — Let the Dockerfile CMD handle execution

## Current flags

!`sayt help launch 2>&1 || true`
!`sayt help integrate 2>&1 || true`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.