[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6NTN7HJB4AVFYXMX4FCFDHN":3,"skill-same:01M6NTN7HJB4AVFYXMX4FCFDHN":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":14,"repositoryHtmlUrl":51,"repositoryHomepageUrl":6,"ownerHtmlUrl":52,"ownerBlogUrl":53,"ownerDescription":54,"updatedAt":55,"install":56,"resources":69},"01M6NTN7HJB4AVFYXMX4FCFDHN","docker-ops",null,"Dockerfile best practices, multi-stage builds, docker-compose, container networking, volume management, and image optimization.","rubicanjr\u002FFinCognis","rubicanjr","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F234912519?v=4","01M6NT156NND8J474G34SB3923","Rubi","01M6NT15B70RWXE1N3YV0BZY8A",0,"MIT","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fdocker-ops\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"docker","ops","docker ops","image","dockerfile","networking","best practices","Optimization","management","container","compose","docker-compose","volume","stage","multi","image optimization","2026-11-10 10:12:30",false,"---\nname: docker-ops\ndescription: Dockerfile best practices, multi-stage builds, docker-compose, container networking, volume management, and image optimization.\n---\n\n# Docker Operations\n\nPractical patterns for building, running, and maintaining containers in production.\n\n## Multi-Stage Builds\n\nMinimize final image size by separating build-time dependencies from runtime.\n\n```dockerfile\n# Stage 1: Builder\nFROM node:20-alpine AS builder\nWORKDIR \u002Fapp\n\n# Copy dependency manifests first (layer cache)\nCOPY package*.json .\u002F\nRUN npm ci --only=production\n\nCOPY tsconfig.json .\u002F\nCOPY src\u002F .\u002Fsrc\u002F\nRUN npm run build\n\n# Stage 2: Runtime (no devDependencies, no source files)\nFROM node:20-alpine AS runtime\nWORKDIR \u002Fapp\n\n# Security: run as non-root\nRUN addgroup -S appgroup && adduser -S appuser -G appgroup\n\nCOPY --from=builder \u002Fapp\u002Fnode_modules .\u002Fnode_modules\nCOPY --from=builder \u002Fapp\u002Fdist .\u002Fdist\n\nUSER appuser\n\nEXPOSE 3000\nCMD [\"node\", \"dist\u002Findex.js\"]\n```\n\n```dockerfile\n# Python multi-stage example\nFROM python:3.12-slim AS builder\nWORKDIR \u002Fapp\n\nRUN pip install --upgrade pip\nCOPY requirements.txt .\nRUN pip install --prefix=\u002Finstall -r requirements.txt\n\nFROM python:3.12-slim AS runtime\nWORKDIR \u002Fapp\n\nCOPY --from=builder \u002Finstall \u002Fusr\u002Flocal\nCOPY src\u002F .\u002Fsrc\u002F\n\nRUN useradd -r -s \u002Fbin\u002Ffalse appuser\nUSER appuser\n\nCMD [\"python\", \"-m\", \"src.main\"]\n```\n\n## Docker Compose for Multi-Service Environments\n\n```yaml\n# docker-compose.yml\nversion: '3.9'\n\nservices:\n  api:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target: runtime\n    ports:\n      - \"3000:3000\"\n    environment:\n      - NODE_ENV=production\n      - DATABASE_URL=postgresql:\u002F\u002Fuser:pass@postgres:5432\u002Fappdb\n      - REDIS_URL=redis:\u002F\u002Fredis:6379\n    depends_on:\n      postgres:\n        condition: service_healthy\n      redis:\n        condition: service_started\n    restart: unless-stopped\n    networks:\n      - internal\n    deploy:\n      resources:\n        limits:\n          memory: 512m\n          cpus: '0.5'\n\n  postgres:\n    image: postgres:16-alpine\n    environment:\n      POSTGRES_USER: user\n      POSTGRES_PASSWORD: pass\n      POSTGRES_DB: appdb\n    volumes:\n      - postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n      - .\u002Finit.sql:\u002Fdocker-entrypoint-initdb.d\u002Finit.sql:ro\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U user -d appdb\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n    networks:\n      - internal\n\n  redis:\n    image: redis:7-alpine\n    volumes:\n      - redis_data:\u002Fdata\n    command: redis-server --appendonly yes\n    networks:\n      - internal\n\nvolumes:\n  postgres_data:\n  redis_data:\n\nnetworks:\n  internal:\n    driver: bridge\n```\n\n## Container Networking\n\n```bash\n# Bridge network (default, same host)\ndocker network create --driver bridge my-network\ndocker run --network my-network --name service-a my-image\n\n# Containers on same bridge network communicate by name\ncurl http:\u002F\u002Fservice-a:3000\u002Fhealth\n\n# Host network (container shares host network stack, Linux only)\ndocker run --network host my-image\n\n# Overlay network (multi-host, Docker Swarm or manual)\ndocker network create --driver overlay --attachable my-overlay\n```\n\n```yaml\n# Compose network isolation example\nservices:\n  frontend:\n    networks:\n      - public      # exposed to outside\n\n  api:\n    networks:\n      - public      # receives traffic from frontend\n      - internal    # talks to DB\n\n  postgres:\n    networks:\n      - internal    # NOT reachable from frontend\n\nnetworks:\n  public:\n  internal:\n    internal: true  # blocks external traffic\n```\n\n## Volume Management\n\n```yaml\n# Named volumes (managed by Docker, persistent)\nvolumes:\n  db_data:          # docker manages path\n  uploads:\n    driver: local\n\nservices:\n  app:\n    volumes:\n      - uploads:\u002Fapp\u002Fuploads         # named volume\n      - .\u002Fconfig:\u002Fapp\u002Fconfig:ro      # bind mount (read-only)\n      - type: tmpfs                  # in-memory, discarded on stop\n        target: \u002Ftmp\n        tmpfs:\n          size: 100m\n```\n\n```bash\n# Volume lifecycle commands\ndocker volume ls\ndocker volume inspect my-volume\ndocker volume rm my-volume\ndocker volume prune           # remove all unused volumes (careful in prod)\n\n# Backup a named volume\ndocker run --rm \\\n  -v my-volume:\u002Fsource:ro \\\n  -v $(pwd):\u002Fbackup \\\n  alpine tar czf \u002Fbackup\u002Fvolume-backup.tar.gz -C \u002Fsource .\n\n# Restore\ndocker run --rm \\\n  -v my-volume:\u002Ftarget \\\n  -v $(pwd):\u002Fbackup \\\n  alpine tar xzf \u002Fbackup\u002Fvolume-backup.tar.gz -C \u002Ftarget\n```\n\n## Health Checks\n\n```dockerfile\n# HEALTHCHECK in Dockerfile\nHEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \\\n  CMD curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth || exit 1\n```\n\n```yaml\n# Override in docker-compose\nservices:\n  api:\n    healthcheck:\n      test: [\"CMD\", \"curl\", \"-f\", \"http:\u002F\u002Flocalhost:3000\u002Fhealth\"]\n      interval: 30s\n      timeout: 10s\n      start_period: 20s\n      retries: 3\n```\n\n```typescript\n\u002F\u002F Express health endpoint\napp.get('\u002Fhealth', (req, res) => {\n  res.json({\n    status: 'ok',\n    uptime: process.uptime(),\n    timestamp: new Date().toISOString()\n  })\n})\n```\n\n## .dockerignore Best Practices\n\n```\n# .dockerignore\nnode_modules\n.git\n.gitignore\n*.log\n*.md\n.env\n.env.*\ncoverage\u002F\ndist\u002F\n.next\u002F\n__pycache__\u002F\n*.pyc\n.pytest_cache\u002F\n.mypy_cache\u002F\nDockerfile*\ndocker-compose*.yml\n.dockerignore\n```\n\n## Image Layer Caching Optimization\n\n```dockerfile\n# BAD: invalidates cache on any source change\nCOPY . .\nRUN npm install\n\n# GOOD: dependency layer cached unless package.json changes\nCOPY package*.json .\u002F\nRUN npm ci\nCOPY . .\nRUN npm run build\n```\n\n```dockerfile\n# Python: cache pip install separately\nCOPY requirements.txt .\nRUN pip install -r requirements.txt   # cached until requirements.txt changes\n\nCOPY . .\n```\n\n## Environment Variable Management\n\n```yaml\n# docker-compose: prefer env_file over inline secrets\nservices:\n  api:\n    env_file:\n      - .env.production         # loaded from file, not committed\n    environment:\n      - NODE_ENV=production     # override specific vars inline\n```\n\n```dockerfile\n# ARG (build-time only, not in final image)\nARG BUILD_VERSION=unknown\nLABEL version=$BUILD_VERSION\n\n# ENV (runtime, visible in container)\nENV PORT=3000\nENV LOG_LEVEL=info\n```\n\n## Docker Secrets vs Env Vars\n\n```yaml\n# Docker Swarm secrets (production approach)\nsecrets:\n  db_password:\n    external: true\n\nservices:\n  api:\n    secrets:\n      - db_password\n    # Secret mounted at \u002Frun\u002Fsecrets\u002Fdb_password (file)\n```\n\n```typescript\n\u002F\u002F Read secret from file (not env var)\nimport { readFileSync } from 'fs'\n\nfunction getSecret(name: string): string {\n  const secretPath = `\u002Frun\u002Fsecrets\u002F${name}`\n  try {\n    return readFileSync(secretPath, 'utf-8').trim()\n  } catch {\n    return process.env[name.toUpperCase()] ?? ''\n  }\n}\n\nconst dbPassword = getSecret('db_password')\n```\n\n## Container Resource Limits\n\n```yaml\n# docker-compose deploy limits\nservices:\n  api:\n    deploy:\n      resources:\n        limits:\n          cpus: '0.5'          # max 50% of one CPU\n          memory: 512m\n        reservations:\n          cpus: '0.25'\n          memory: 256m\n```\n\n```bash\n# docker run resource flags\ndocker run \\\n  --memory=\"512m\" \\\n  --memory-swap=\"1g\" \\\n  --cpus=\"0.5\" \\\n  my-image\n```\n\n## Image Scanning for Vulnerabilities\n\n```bash\n# Trivy (recommended, free)\ntrivy image my-app:latest\ntrivy image --severity HIGH,CRITICAL my-app:latest\ntrivy image --exit-code 1 --severity CRITICAL my-app:latest  # fail on critical\n\n# Snyk (SaaS, requires account)\nsnyk container test my-app:latest\n\n# Docker Scout (built-in with Docker Desktop)\ndocker scout cves my-app:latest\ndocker scout recommendations my-app:latest\n```\n\n## Debugging Containers\n\n```bash\n# View logs\ndocker logs -f container-name\ndocker logs --tail 100 container-name\ndocker compose logs -f service-name\n\n# Exec into running container\ndocker exec -it container-name sh\ndocker exec -it container-name bash\n\n# Inspect container metadata\ndocker inspect container-name\ndocker inspect --format '{{.NetworkSettings.IPAddress}}' container-name\n\n# Check resource usage\ndocker stats\ndocker stats --no-stream container-name\n\n# Copy files to\u002Ffrom container\ndocker cp container-name:\u002Fapp\u002Flogs\u002Ferror.log .\u002Ferror.log\ndocker cp .\u002Fconfig.json container-name:\u002Fapp\u002Fconfig.json\n```\n\n## PID 1 Problem and Signal Handling (tini)\n\n```dockerfile\n# BAD: Node.js as PID 1 does not forward signals properly\nCMD [\"node\", \"dist\u002Findex.js\"]\n\n# GOOD: use tini as init process\nRUN apk add --no-cache tini\nENTRYPOINT [\"\u002Fsbin\u002Ftini\", \"--\"]\nCMD [\"node\", \"dist\u002Findex.js\"]\n```\n\n```yaml\n# docker-compose init option (Docker 18.06+)\nservices:\n  api:\n    init: true      # uses tini automatically\n```\n\n```typescript\n\u002F\u002F Graceful shutdown in Node.js\nprocess.on('SIGTERM', async () => {\n  console.log('SIGTERM received, shutting down gracefully')\n  await server.close()\n  await db.disconnect()\n  process.exit(0)\n})\n```\n\n## Common Pitfalls\n\n```dockerfile\n# AVOID: running as root\n# USER root  ← dangerous\n\n# AVOID: latest tag in production\n# FROM node:latest  ← use specific version\n\n# AVOID: installing dev tools in production image\n# RUN apt-get install -y vim curl wget\n\n# AVOID: secrets in build args (visible in image history)\n# ARG SECRET_KEY=abc123\n\n# PREFER: specific digest for reproducible builds\nFROM node:20.11.0-alpine@sha256:abc123...\n```\n\n**Key principle**: Keep images small, run as non-root, pin versions, use multi-stage builds, always define health checks.\n","# Docker Operations\n\nPractical patterns for building, running, and maintaining containers in production.\n\n## Multi-Stage Builds\n\nMinimize final image size by separating build-time dependencies from runtime.\n\n```dockerfile\n# Stage 1: Builder\nFROM node:20-alpine AS builder\nWORKDIR \u002Fapp\n\n# Copy dependency manifests first (layer cache)\nCOPY package*.json .\u002F\nRUN npm ci --only=production\n\nCOPY tsconfig.json .\u002F\nCOPY src\u002F .\u002Fsrc\u002F\nRUN npm run build\n\n# Stage 2: Runtime (no devDependencies, no source files)\nFROM node:20-alpine AS runtime\nWORKDIR \u002Fapp\n\n# Security: run as non-root\nRUN addgroup -S appgroup && adduser -S appuser -G appgroup\n\nCOPY --from=builder \u002Fapp\u002Fnode_modules .\u002Fnode_modules\nCOPY --from=builder \u002Fapp\u002Fdist .\u002Fdist\n\nUSER appuser\n\nEXPOSE 3000\nCMD [\"node\", \"dist\u002Findex.js\"]\n```\n\n```dockerfile\n# Python multi-stage example\nFROM python:3.12-slim AS builder\nWORKDIR \u002Fapp\n\nRUN pip install --upgrade pip\nCOPY requirements.txt .\nRUN pip install --prefix=\u002Finstall -r requirements.txt\n\nFROM python:3.12-slim AS runtime\nWORKDIR \u002Fapp\n\nCOPY --from=builder \u002Finstall \u002Fusr\u002Flocal\nCOPY src\u002F .\u002Fsrc\u002F\n\nRUN useradd -r -s \u002Fbin\u002Ffalse appuser\nUSER appuser\n\nCMD [\"python\", \"-m\", \"src.main\"]\n```\n\n## Docker Compose for Multi-Service Environments\n\n```yaml\n# docker-compose.yml\nversion: '3.9'\n\nservices:\n  api:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target: runtime\n    ports:\n      - \"3000:3000\"\n    environment:\n      - NODE_ENV=production\n      - DATABASE_URL=postgresql:\u002F\u002Fuser:pass@postgres:5432\u002Fappdb\n      - REDIS_URL=redis:\u002F\u002Fredis:6379\n    depends_on:\n      postgres:\n        condition: service_healthy\n      redis:\n        condition: service_started\n    restart: unless-stopped\n    networks:\n      - internal\n    deploy:\n      resources:\n        limits:\n          memory: 512m\n          cpus: '0.5'\n\n  postgres:\n    image: postgres:16-alpine\n    environment:\n      POSTGRES_USER: user\n      POSTGRES_PASSWORD: pass\n      POSTGRES_DB: appdb\n    volumes:\n      - postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n      - .\u002Finit.sql:\u002Fdocker-entrypoint-initdb.d\u002Finit.sql:ro\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U user -d appdb\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n    networks:\n      - internal\n\n  redis:\n    image: redis:7-alpine\n    volumes:\n      - redis_data:\u002Fdata\n    command: redis-server --appendonly yes\n    networks:\n      - internal\n\nvolumes:\n  postgres_data:\n  redis_data:\n\nnetworks:\n  internal:\n    driver: bridge\n```\n\n## Container Networking\n\n```bash\n# Bridge network (default, same host)\ndocker network create --driver bridge my-network\ndocker run --network my-network --name service-a my-image\n\n# Containers on same bridge network communicate by name\ncurl http:\u002F\u002Fservice-a:3000\u002Fhealth\n\n# Host network (container shares host network stack, Linux only)\ndocker run --network host my-image\n\n# Overlay network (multi-host, Docker Swarm or manual)\ndocker network create --driver overlay --attachable my-overlay\n```\n\n```yaml\n# Compose network isolation example\nservices:\n  frontend:\n    networks:\n      - public      # exposed to outside\n\n  api:\n    networks:\n      - public      # receives traffic from frontend\n      - internal    # talks to DB\n\n  postgres:\n    networks:\n      - internal    # NOT reachable from frontend\n\nnetworks:\n  public:\n  internal:\n    internal: true  # blocks external traffic\n```\n\n## Volume Management\n\n```yaml\n# Named volumes (managed by Docker, persistent)\nvolumes:\n  db_data:          # docker manages path\n  uploads:\n    driver: local\n\nservices:\n  app:\n    volumes:\n      - uploads:\u002Fapp\u002Fuploads         # named volume\n      - .\u002Fconfig:\u002Fapp\u002Fconfig:ro      # bind mount (read-only)\n      - type: tmpfs                  # in-memory, discarded on stop\n        target: \u002Ftmp\n        tmpfs:\n          size: 100m\n```\n\n```bash\n# Volume lifecycle commands\ndocker volume ls\ndocker volume inspect my-volume\ndocker volume rm my-volume\ndocker volume prune           # remove all unused volumes (careful in prod)\n\n# Backup a named volume\ndocker run --rm \\\n  -v my-volume:\u002Fsource:ro \\\n  -v $(pwd):\u002Fbackup \\\n  alpine tar czf \u002Fbackup\u002Fvolume-backup.tar.gz -C \u002Fsource .\n\n# Restore\ndocker run --rm \\\n  -v my-volume:\u002Ftarget \\\n  -v $(pwd):\u002Fbackup \\\n  alpine tar xzf \u002Fbackup\u002Fvolume-backup.tar.gz -C \u002Ftarget\n```\n\n## Health Checks\n\n```dockerfile\n# HEALTHCHECK in Dockerfile\nHEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \\\n  CMD curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth || exit 1\n```\n\n```yaml\n# Override in docker-compose\nservices:\n  api:\n    healthcheck:\n      test: [\"CMD\", \"curl\", \"-f\", \"http:\u002F\u002Flocalhost:3000\u002Fhealth\"]\n      interval: 30s\n      timeout: 10s\n      start_period: 20s\n      retries: 3\n```\n\n```typescript\n\u002F\u002F Express health endpoint\napp.get('\u002Fhealth', (req, res) => {\n  res.json({\n    status: 'ok',\n    uptime: process.uptime(),\n    timestamp: new Date().toISOString()\n  })\n})\n```\n\n## .dockerignore Best Practices\n\n```\n# .dockerignore\nnode_modules\n.git\n.gitignore\n*.log\n*.md\n.env\n.env.*\ncoverage\u002F\ndist\u002F\n.next\u002F\n__pycache__\u002F\n*.pyc\n.pytest_cache\u002F\n.mypy_cache\u002F\nDockerfile*\ndocker-compose*.yml\n.dockerignore\n```\n\n## Image Layer Caching Optimization\n\n```dockerfile\n# BAD: invalidates cache on any source change\nCOPY . .\nRUN npm install\n\n# GOOD: dependency layer cached unless package.json changes\nCOPY package*.json .\u002F\nRUN npm ci\nCOPY . .\nRUN npm run build\n```\n\n```dockerfile\n# Python: cache pip install separately\nCOPY requirements.txt .\nRUN pip install -r requirements.txt   # cached until requirements.txt changes\n\nCOPY . .\n```\n\n## Environment Variable Management\n\n```yaml\n# docker-compose: prefer env_file over inline secrets\nservices:\n  api:\n    env_file:\n      - .env.production         # loaded from file, not committed\n    environment:\n      - NODE_ENV=production     # override specific vars inline\n```\n\n```dockerfile\n# ARG (build-time only, not in final image)\nARG BUILD_VERSION=unknown\nLABEL version=$BUILD_VERSION\n\n# ENV (runtime, visible in container)\nENV PORT=3000\nENV LOG_LEVEL=info\n```\n\n## Docker Secrets vs Env Vars\n\n```yaml\n# Docker Swarm secrets (production approach)\nsecrets:\n  db_password:\n    external: true\n\nservices:\n  api:\n    secrets:\n      - db_password\n    # Secret mounted at \u002Frun\u002Fsecrets\u002Fdb_password (file)\n```\n\n```typescript\n\u002F\u002F Read secret from file (not env var)\nimport { readFileSync } from 'fs'\n\nfunction getSecret(name: string): string {\n  const secretPath = `\u002Frun\u002Fsecrets\u002F${name}`\n  try {\n    return readFileSync(secretPath, 'utf-8').trim()\n  } catch {\n    return process.env[name.toUpperCase()] ?? ''\n  }\n}\n\nconst dbPassword = getSecret('db_password')\n```\n\n## Container Resource Limits\n\n```yaml\n# docker-compose deploy limits\nservices:\n  api:\n    deploy:\n      resources:\n        limits:\n          cpus: '0.5'          # max 50% of one CPU\n          memory: 512m\n        reservations:\n          cpus: '0.25'\n          memory: 256m\n```\n\n```bash\n# docker run resource flags\ndocker run \\\n  --memory=\"512m\" \\\n  --memory-swap=\"1g\" \\\n  --cpus=\"0.5\" \\\n  my-image\n```\n\n## Image Scanning for Vulnerabilities\n\n```bash\n# Trivy (recommended, free)\ntrivy image my-app:latest\ntrivy image --severity HIGH,CRITICAL my-app:latest\ntrivy image --exit-code 1 --severity CRITICAL my-app:latest  # fail on critical\n\n# Snyk (SaaS, requires account)\nsnyk container test my-app:latest\n\n# Docker Scout (built-in with Docker Desktop)\ndocker scout cves my-app:latest\ndocker scout recommendations my-app:latest\n```\n\n## Debugging Containers\n\n```bash\n# View logs\ndocker logs -f container-name\ndocker logs --tail 100 container-name\ndocker compose logs -f service-name\n\n# Exec into running container\ndocker exec -it container-name sh\ndocker exec -it container-name bash\n\n# Inspect container metadata\ndocker inspect container-name\ndocker inspect --format '{{.NetworkSettings.IPAddress}}' container-name\n\n# Check resource usage\ndocker stats\ndocker stats --no-stream container-name\n\n# Copy files to\u002Ffrom container\ndocker cp container-name:\u002Fapp\u002Flogs\u002Ferror.log .\u002Ferror.log\ndocker cp .\u002Fconfig.json container-name:\u002Fapp\u002Fconfig.json\n```\n\n## PID 1 Problem and Signal Handling (tini)\n\n```dockerfile\n# BAD: Node.js as PID 1 does not forward signals properly\nCMD [\"node\", \"dist\u002Findex.js\"]\n\n# GOOD: use tini as init process\nRUN apk add --no-cache tini\nENTRYPOINT [\"\u002Fsbin\u002Ftini\", \"--\"]\nCMD [\"node\", \"dist\u002Findex.js\"]\n```\n\n```yaml\n# docker-compose init option (Docker 18.06+)\nservices:\n  api:\n    init: true      # uses tini automatically\n```\n\n```typescript\n\u002F\u002F Graceful shutdown in Node.js\nprocess.on('SIGTERM', async () => {\n  console.log('SIGTERM received, shutting down gracefully')\n  await server.close()\n  await db.disconnect()\n  process.exit(0)\n})\n```\n\n## Common Pitfalls\n\n```dockerfile\n# AVOID: running as root\n# USER root  ← dangerous\n\n# AVOID: latest tag in production\n# FROM node:latest  ← use specific version\n\n# AVOID: installing dev tools in production image\n# RUN apt-get install -y vim curl wget\n\n# AVOID: secrets in build args (visible in image history)\n# ARG SECRET_KEY=abc123\n\n# PREFER: specific digest for reproducible builds\nFROM node:20.11.0-alpine@sha256:abc123...\n```\n\n**Key principle**: Keep images small, run as non-root, pin versions, use multi-stage builds, always define health checks.\n",{"name":5,"description":7},"main","70e1c350500ee750987420bbae48837afa906b7e","skills\u002Fdocker-ops","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis","https:\u002F\u002Fgithub.com\u002Frubicanjr","linktr.ee\u002Ffinanskodu","Industrial Engineer & CIO @FinansKodu. Architecting AI-driven financial agents and autonomous workflows. 🏗️ Building systems, not just prompts.","2026-06-09 13:50:15",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add rubicanjr\u002FFinCognis --skill docker-ops",{"tool":62,"command":63},"bunx","bunx skills add rubicanjr\u002FFinCognis --skill docker-ops",{"tool":65,"command":66},"pnpm","pnpm dlx skills add rubicanjr\u002FFinCognis --skill docker-ops","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fdocker-ops\u002FSKILL.md",true,[],[71,90,99,118,148,167],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":80,"publishedAt":43},"01M6NWS9DAXJTADT4T5MB49X8W","workflow-router","Goal-based workflow orchestration - routes tasks to specialist agents based on user goals","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow-router\u002FSKILL.md",[77],{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[81,82,83,84,85,86,87,88,89],"workflow router","workflow","router","orchestration","goals","goal","user","workflow orchestration","tasks",{"skillNo":91,"name":92,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":96,"publishedAt":43},"01M6NWS3RETSC0SHP16HP66E14","wiring","Wiring Verification","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fwiring\u002FSKILL.md",[],[92,97,98],"verification","false",{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":108,"publishedAt":43},"01M6NWS0XW9CVQZBDA1EGW0TWC","websocket-patterns","Connection management, room patterns, reconnection strategies, message buffering, and binary protocol design.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fwebsocket-patterns\u002Fskill.md",[105],{"categoryNo":106,"categoryName":107,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",[109,110,111,112,113,35,114,115,116,117],"websocket","patterns","websocket patterns","design","protocol","message","strategies","binary","connection",{"skillNo":119,"name":120,"nameZh":6,"description":121,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":128,"publishedAt":43},"01M6NWRDQDX9ZNETAWRRMQWV52","visual-verdict","Screenshot comparison QA for frontend development. Takes a screenshot of the current implementation, scores it across multiple visual dimensions, and returns a structured PASS\u002FREVISE\u002FFAIL verdict with concrete fixes. Use when implementing UI from a design reference or verifying visual correctness.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fvisual-verdict\u002FSKILL.md",[124,127],{"categoryNo":125,"categoryName":126,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},[129,130,131,132,133,134,135,136,137,138,139,140,141,112,142,143,144,145,146,147],"visual","verdict","visual verdict","ui","across","revise","comparison","correctness","implementing","for","fixes","dimensions","frontend","development","qa","screenshot","returns","use","frontend development",{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":154,"publishedAt":43},"01M6NWR8HSNRS7MNDK2J02QYMN","verification-loop","Comprehensive verification system covering build, types, lint, tests, security, and diff review before a PR.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fverification-loop\u002FSKILL.md",[],[155,156,97,157,158,159,160,161,162,163,164,165,166],"verification loop","loop","lint","review","security","pr","system","diff","build","tests","diff review","types",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":173,"publishedAt":43},"01M6NWR4HHSFR0VR5EXFRWA747","vector-db-patterns","Embedding strategies, ANN algorithms, hybrid search, RAG chunking strategies, and reranking for semantic search and retrieval.","https:\u002F\u002Fgithub.com\u002Frubicanjr\u002FFinCognis\u002Fblob\u002Fmain\u002Fskills\u002Fvector-db-patterns\u002Fskill.md",[],[174,175,176,177,110,178,179,180,181,115,182,183,184,185,186,138,187,188],"vector db patterns","vector db","db","vector","semantic search","search","embedding strategies","algorithms","chunking","semantic","retrieval","embedding","Hybrid Search","hybrid","rag"]