<!-- skill: Docker Deploy | version: 1.0.0 | description: Containerize apps with Docker — Dockerfiles, compose, registries, updates | category: System & Productivity -->

## Instructions
- One service per container; use `docker compose` to wire app + database + proxy together.
- Write minimal Dockerfiles: official slim base image, `WORKDIR`, copy dependency files first (layer cache), then code. Never `COPY .` before `npm install`.
- Config via environment variables, secrets via env files (`--env-file`), never baked into images. `.dockerignore` node_modules, .git, .env.
- Pin base image tags (`node:20-slim`, not `node:latest`). Expose only needed ports.
- Persist data in named volumes, never inside the container filesystem.
- Update flow: `docker compose pull && docker compose up -d --build`, verify with `docker compose ps` + logs.

## Examples
- Node app + Postgres: two services, app waits on `db:5432`, data in `pgdata` volume.
- Static site: `nginx:alpine` serving a `dist/` folder, port 80.

## Workflows
1. Write `.dockerignore`, then `Dockerfile` (deps layer first).
2. `docker build -t name .` → `docker run -p` → verify locally.
3. Add `compose.yaml` with env, volumes, restart policy.
4. Push to a registry for other machines (`docker tag` + `push`).
5. On the Pi/server: compose file + `up -d`, check logs, set restart `: unless-stopped`.

## Templates
```dockerfile
# Node app — cached deps layer, slim runtime
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
```
```yaml
# compose.yaml — app + postgres
services:
  app:
    build: .
    ports: ["3000:3000"]
    env_file: .env
    restart: unless-stopped
  db:
    image: postgres:16
    volumes: ["pgdata:/var/lib/postgresql/data"]
    restart: unless-stopped
volumes:
  pgdata:
```

## Tools
- SARAH `shell_run`: `docker compose up -d --build`, `docker compose logs --tail=50`.
- Check the Pi has disk room first: `df -h` (images are heavy on SD cards).

## Advanced
- Multi-stage builds: `AS build` (full toolchain) → copy artifacts into slim runtime — halves image size, drops dev deps from production.
- Health: `HEALTHCHECK CMD curl -f http://localhost:3000/api/health || exit 1` + `depends_on: condition: service_healthy` so apps start in order.
- Limits: `deploy.resources.limits` (memory/CPU) per service — one leaky container shouldn't take the Pi down. Log rotation: `logging: {options: {max-size: "10m", max-file: "3"}}`.
- Secrets: Docker secrets (swarm) or env files with `chmod 600`; never `ENV SECRET=` in Dockerfiles (baked into layers forever).
- Compose profiles (`--profile gpu`) for optional services; `.env` per environment (dev/staging/prod).
- Backups: stop container OR use volume snapshots; `docker run --volumes-from` + tar for live volume backup.
- Troubleshooting: rebuild not picking changes → check layer order/`--no-cache`; can't reach service → same compose network? use service names as hostnames; disk full → `docker system prune -a --volumes` (careful: deletes unused volumes).

## Documentation
- Dockerfile best practices: https://docs.docker.com/build/building/best-practices
- Compose reference: https://docs.docker.com/compose
