Skip to content
Back to skills

Devops

BSecurity

Containers, infrastructure and CI/CD — Dockerfiles, Docker Compose, Kubernetes, Terraform, GitHub Actions hardening (permissions, SHA pinning, OIDC, injection), CI caching, deploy strategies and rollback, observability, troubleshooting. Use for Dockerfile, k8s manifests, .tf, workflows, failed deploys; "докер", "деплой", "пайплайн".

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 13, 2026
devopsrustgonodedockerkubernetesazureterraformgitapidatabase

Works with

  • cli
  • api

Security analysis

B88/100
  • criticalAccesses sensitive system or user directories

Pro scans all 4 files and shows the line behind each finding

Scanned October 13, 2026

npx -y skills add dmminga1983/minga --skill devops --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Devops?

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

Security grade badge for Devops
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/dmminga1983-devops/badge)](https://www.skillsdirectory.com/skills/dmminga1983-devops)

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: devops
description: Containers, infrastructure and CI/CD — Dockerfiles, Docker Compose, Kubernetes, Terraform, GitHub Actions hardening (permissions, SHA pinning, OIDC, injection), CI caching, deploy strategies and rollback, observability, troubleshooting. Use for Dockerfile, k8s manifests, .tf, workflows, failed deploys; "докер", "деплой", "пайплайн".
paths: "**/Dockerfile*, **/*.dockerfile, **/.dockerignore, **/docker-compose*.yml, **/compose*.yaml, **/*.tf, **/*.tfvars, .github/workflows/*.yml, .github/workflows/*.yaml, **/k8s/**, **/helm/**, **/charts/**, **/kustomization.yaml"
---

Reply in the user's language.

Infrastructure code runs with real credentials against real systems. Plan, review and preview before applying; label anything destructive; never paste secrets into files, logs or chat.

## 1. Detect the project

- **Containers:** `Dockerfile*`, `.dockerignore`, `compose.yaml`/`docker-compose*.yml`, base image tags and digests, build scripts.
- **Orchestration:** `k8s/`, `manifests/`, Helm `Chart.yaml`/`values*.yaml`, `kustomization.yaml`, Argo CD/Flux config; target cluster version (`kubectl version`).
- **IaC:** `*.tf`, `.terraform.lock.hcl`, `backend` blocks, `required_version`, Terraform vs OpenTofu, workspaces/environments layout.
- **CI/CD:** `.github/workflows/`, `dependabot.yml`, `CODEOWNERS`, environments and deploy jobs; other CI systems if present.
- **Observability:** logging library, metrics/tracing SDK (OpenTelemetry?), dashboards and alert rules in the repo.

Rule: *follow the project's existing conventions (tools, layout, naming, environments) over these defaults.*

## 2. Defaults for new work

- Small official or distroless base images, **pinned by tag and digest**; multi-stage builds; non-root user.
- Compose for local development only; Kubernetes (or a managed container platform) for production.
- Terraform/OpenTofu with a remote backend that locks; one state per environment and blast radius.
- GitHub Actions: top-level `permissions: contents: read`, full-SHA-pinned actions, OIDC for cloud access, environments with required reviewers for production.
- Versions: check the current supported release of each tool/runtime/image instead of assuming one; never deploy `:latest`.
- Same artifact (image digest) promoted through environments; config differs, the build does not.

## 3. Dockerfile

- **Base:** slim/distroless/official images; pin `image:tag@sha256:digest` and let Dependabot/Renovate bump it. Alpine (musl) can break native wheels/binaries — choose deliberately.
- **Multi-stage:** build tools and dev dependencies stay in the build stage; the final stage gets only runtime files.
- **Layer caching:** copy dependency manifests and lockfiles first, install, then copy the source. Use BuildKit cache mounts (`RUN --mount=type=cache,target=…`) for package caches.
- **`.dockerignore`:** exclude `.git`, `.env*`, `node_modules`, build output, local databases, keys, test data — keeps context small and secrets out.
- **Non-root:** `USER` with a fixed UID (or the image's `node`/`nonroot` user); files the app writes go to a dedicated writable dir. Lets Kubernetes enforce `runAsNonRoot`.
- **No secrets in images:** not in `COPY`, `ENV` or `ARG` — build args and env are visible in image history/metadata. Use `RUN --mount=type=secret,id=…` for build-time secrets and runtime env/secret mounts for runtime.
- **Exec-form** `CMD`/`ENTRYPOINT` (`["node", "server.js"]`) so the process gets signals; handle `SIGTERM` gracefully; use an init (`docker run --init`, `tini`) if the app spawns children.
- **`HEALTHCHECK`** for Compose/standalone Docker (Kubernetes ignores it — use probes).
- One process per container; logs to stdout/stderr; immutable config via env.
- Lint with `hadolint`; scan images with Trivy, Grype or Docker Scout in CI.

```dockerfile
# syntax=docker/dockerfile:1
# Set to your supported LTS; in production also pin the digest: node:<tag>@sha256:<digest>
ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:${NODE_VERSION}-bookworm-slim
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/package.json ./
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=15s --retries=3 CMD ["node", "dist/healthcheck.js"]
CMD ["node", "dist/server.js"]
```

Build-time secret: `RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci` with `docker build --secret id=npmrc,src=.npmrc .` — the file is never written into a layer.

## 4. Docker Compose (local development)

- File `compose.yaml`; the top-level `version:` key is obsolete — omit it.
- `depends_on` with `condition: service_healthy` plus a `healthcheck` on the database, instead of sleep loops.
- Secrets from an untracked `.env` (commit `.env.example` with placeholders); never real credentials in the YAML.
- Publish ports on localhost only (`"127.0.0.1:5432:5432"`); Docker-published ports bypass host firewalls such as `ufw`.
- Named volumes for data; bind mounts for source; `develop.watch` / `docker compose watch` for hot reload.
- `profiles:` for optional services (workers, admin tools). Pin service images the same way as in production.

## 5. Kubernetes basics

Details and manifests: [kubernetes.md](kubernetes.md).

- **Requests and limits:** set CPU and memory requests on every container (scheduling depends on them) and a memory limit (exceeding it → `OOMKilled`). CPU limits cause throttling — set them deliberately.
- **Probes:** readiness = "can take traffic now" (dependency-aware, cheap); liveness = "process is wedged, restart me" (never checks dependencies, or a database blip restarts everything); startup probe for slow boots.
- **ConfigMap vs Secret:** non-sensitive config in ConfigMaps; credentials in Secrets — which are only base64-encoded, so enable encryption at rest, restrict RBAC on them, and prefer an external secret manager (External Secrets, CSI driver, sealed/SOPS-encrypted secrets in Git).
- **Rolling updates:** `maxSurge`/`maxUnavailable`, readiness gating, `PodDisruptionBudget`, graceful shutdown (`preStop` + `terminationGracePeriodSeconds`). `kubectl rollout status|history|undo`.
- **Security context:** `runAsNonRoot`, `allowPrivilegeEscalation: false`, `readOnlyRootFilesystem: true`, drop all capabilities, `seccompProfile: RuntimeDefault`; enforce with Pod Security Admission `restricted`.
- **RBAC least privilege:** namespaced `Role` over `ClusterRole`, no wildcards, a dedicated ServiceAccount per workload, `automountServiceAccountToken: false` when the pod doesn't call the API. Verify with `kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa>`.
- NetworkPolicies default-deny, then allow what is needed. Immutable image references (digest or unique tag).

## 6. Terraform

Details: [terraform.md](terraform.md).

- **Remote state with locking** (S3 with `use_lockfile`, GCS, azurerm, Terraform Cloud/HCP, or equivalent); state bucket encrypted, versioned, access-restricted. Never commit `*.tfstate`.
- **Plan before apply:** `terraform plan -out=tfplan`, review (in the PR), then `terraform apply tfplan`. Read every `destroy` and `forces replacement`.
- **Modules** for repeated patterns with typed, validated variables; pin module and provider versions; commit `.terraform.lock.hcl`.
- **Secrets:** state stores attribute values in plain text, `sensitive = true` only hides CLI output. Generate/store secrets in a secret manager and pass references; use ephemeral values/write-only arguments where the version and provider support them; treat state as secret.
- **Drift:** `terraform plan -refresh-only` on a schedule; fix by changing code, not the console. Refactor with `moved`/`import`/`removed` blocks instead of state surgery.
- `fmt -check`, `validate`, a linter (TFLint) and a policy/security scanner (Checkov, Trivy) in CI.

## 7. GitHub Actions hardening

Details and a hardened template: [github-actions.md](github-actions.md). Consistent with `/minga:secure-publish`.

- Top-level `permissions: contents: read`; widen per job only (`id-token: write`, `packages: write`, `pull-requests: write`).
- **Never `pull_request_target` (or `workflow_run` on PR artifacts) together with checking out or running PR code** — it runs untrusted code with secrets and a write token.
- **No untrusted `${{ github.event.* }}` / `github.head_ref` inside `run:`** — pass through `env:` and quote:
  ```yaml
  - run: echo "Title: $PR_TITLE"
    env:
      PR_TITLE: ${{ github.event.pull_request.title }}
  ```
- **Pin third-party actions to a full commit SHA** with a version comment; let Dependabot update them. Prefer few, well-maintained actions.
- **OIDC** (`id-token: write`) to assume short-lived cloud roles; no long-lived cloud keys in secrets. Scope the trust policy to repo + branch/environment.
- **Environments** with required reviewers and branch restrictions for production; environment-scoped secrets.
- `actions/checkout` with `persist-credentials: false` unless the job pushes; `concurrency` groups for deploys; `timeout-minutes` on jobs.
- `CODEOWNERS` on `.github/workflows/`; lint workflows with `actionlint` and `zizmor`.

## 8. CI caching

- Cache by lockfile hash: `actions/cache` with `key: ${{ runner.os }}-x-${{ hashFiles('**/package-lock.json') }}`, or `setup-*` actions' built-in `cache:` input.
- Docker layer cache: Buildx with `cache-from: type=gha` / `cache-to: type=gha,mode=max`, or a registry cache.
- Cache dependencies, not build outputs you then trust blindly; release/publish workflows should build from a clean state or caches only the default branch wrote (cache poisoning).
- Never cache directories that contain credentials (`~/.npmrc` with tokens, `~/.docker/config.json`).

## 9. Deploy and rollback

| Strategy | How | Good for | Cost |
|---|---|---|---|
| Rolling | replace instances gradually | default, stateless services | both versions live together |
| Blue/green | full second environment, switch traffic | instant rollback, big changes | double capacity |
| Canary | small % of traffic first, widen on healthy metrics | risky changes, high traffic | needs traffic splitting + good metrics |

- Every release is deployable **and** reversible: previous image digest known, config versioned, DB migrations backward-compatible (expand/contract — see `/minga-data:data`).
- Automate rollback triggers on symptom metrics (error rate, latency) during canary/rollout.
- Feature flags decouple deploy from release. Smoke tests after deploy. Release checklist: `/minga:ship`.

## 10. Observability

- **Structured logs** (JSON) to stdout with timestamp, level, service, version, request/trace ID; no secrets, tokens or unnecessary PII.
- **Metrics:** RED for services (rate, errors, duration as histograms), USE for resources (utilisation, saturation, errors). Watch label cardinality — no user IDs or URLs with IDs as labels.
- **Traces:** OpenTelemetry SDK + collector; propagate context across HTTP/queues; sample sensibly.
- **Alerts on symptoms users feel** (error rate, latency, SLO burn rate), not on causes like CPU; every alert actionable with a runbook link. Dashboards per service; deploy markers.

## 11. Security pitfalls

Secrets in images, build args, Compose files, Terraform state in Git, CI logs (`set -x`, `env` dumps); containers as root or `privileged`; `hostPath`/Docker socket mounts; public databases, dashboards or Kubernetes API; `cluster-admin` service accounts; wildcard RBAC/IAM (`*:*`); unpinned actions and images; `pull_request_target` misuse; self-hosted runners on public repositories; long-lived cloud keys; disabled TLS verification.

## 12. Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| `CrashLoopBackOff` | App exits on start: bad config/env, missing secret, failing migration, liveness probe killing a slow start | `kubectl logs <pod> --previous`, `kubectl describe pod`; fix config; add a startup probe |
| `ImagePullBackOff` / `ErrImagePull` | Wrong name/tag/digest, private registry without `imagePullSecrets`, wrong architecture, rate limit | `kubectl describe pod` events; verify the image exists for the node's arch; add pull secret |
| `OOMKilled` (exit code 137) | Memory limit below real usage, leak, runtime heap not sized to the container | Measure, raise limit/request, cap heap (e.g. JVM `-XX:MaxRAMPercentage`, Node `--max-old-space-size`) |
| Pod `Pending` | Requests exceed free capacity, node selector/taint mismatch, unbound PVC | `kubectl describe pod` → events; adjust requests or capacity |
| `bind: address already in use` / connection refused | Port taken on host; app listens on `127.0.0.1` inside container | Change host port; listen on `0.0.0.0` in the container; check `containerPort`/Service `targetPort` |
| `permission denied` in container | Non-root user can't write to a root-owned path or bind mount; read-only root FS | `COPY --chown`, writable `emptyDir`/volume, `fsGroup`; fix host UID/GID for bind mounts |
| `exec format error` | Image built for another CPU architecture | `docker buildx build --platform linux/amd64,linux/arm64` |
| Readiness never passes, no traffic | Wrong probe path/port, probe hits a dependency that's down | Probe the real port; make readiness cheap |
| CI flaky | Test order/time/network dependence, shared state, resource limits, cache keyed wrongly | Reproduce with the same seed; isolate tests; fix cache keys; quarantine with an issue, don't blind-retry |
| `Error acquiring the state lock` | Another run holds the lock, or a crashed run left it | Confirm no apply is running, then `terraform force-unlock <LOCK_ID>` (**dangerous** if one is) |
| Terraform wants to replace a resource unexpectedly | Changed an immutable attribute, renamed resource, provider upgrade | Read `forces replacement`; use `moved` blocks; pin providers |
| Action fails `Resource not accessible by integration` | `GITHUB_TOKEN` permissions too narrow, or fork PR (read-only token) | Grant the specific permission on that job; don't switch to `pull_request_target` |

## 13. Review checklist

- [ ] Images: pinned base, multi-stage, non-root, `.dockerignore`, no secrets in layers/args, healthcheck/probes.
- [ ] Kubernetes: requests/limits, readiness/liveness/startup probes, security context, least-privilege RBAC, Secrets not in plain Git.
- [ ] Terraform: remote locked state, plan reviewed, versions pinned, no secrets in code, no unexpected destroy/replace.
- [ ] Workflows: `permissions: contents: read`, no `pull_request_target` + PR checkout, no event data in `run:`, SHA-pinned actions, OIDC, protected environments.
- [ ] Deploy: rollback path known and tested, migrations compatible, smoke tests and symptom alerts in place.

Files in this skill

  • SKILL.md14.6 KB
  • github-actions.md9.2 KB
  • kubernetes.md9.6 KB
  • terraform.md8 KB

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.