{"name":"containerise-app-with-docker","description":"Packages an app into a small, secure Docker image with a multi-stage Dockerfile - pinned slim base images, cached dependency layers, a .dockerignore, a non-root user and a health check - then builds and runs it locally. Use when the user wants to dockerise an app, write a Dockerfile, shrink a huge image, or run their app in a container.","category":"deployment","tags":["docker","hosting","security"],"models":["any"],"proven":false,"passing_families":0,"model_testing":"Not yet model-tested; testing Jan 2027","url":"https://theskillvault.ai/skills/containerise-app-with-docker","api_url":"https://theskillvault.ai/api/skills/containerise-app-with-docker","raw_url":"https://theskillvault.ai/api/skills/containerise-app-with-docker/raw","body":"# Containerise an app with Docker\n\nA container packs your app and everything it needs into one image that runs the same everywhere. Multi-stage builds keep that image small.\n\n## When to use\n- A host asks for a Docker image or a Dockerfile.\n- \"It works on my machine\" keeps biting you.\n- Your image is over 1 GB and slow to deploy.\n\n## Steps\n1. Add a `.dockerignore` listing `node_modules`, `.git`, `.env*`, build output and logs.\n2. Pick an official slim base with an exact tag, such as `node:22-slim` or `python:3.12-slim`.\n3. Stage one (\"deps\"): copy only the lockfile and manifest, then install dependencies. This layer caches.\n4. Stage two (\"build\"): copy the source and run the build.\n5. Final stage: start from the slim base again and copy only the built output and production dependencies.\n6. Create or use a non-root user and switch to it with `USER`.\n7. Set `NODE_ENV=production` (or the equivalent), `EXPOSE` the port and add a `HEALTHCHECK` if your host uses it.\n8. Build and run: `docker build -t myapp .` then `docker run --env-file .env -p 3000:3000 myapp`.\n9. Check the size with `docker images myapp` and scan it with `docker scout cves myapp` or Trivy.\n\n```dockerfile\nFROM node:22-slim AS deps\nWORKDIR /app\nCOPY package.json package-lock.json ./\nRUN npm ci\n\nFROM deps AS build\nCOPY . .\nRUN npm run build && npm prune --omit=dev\n\nFROM node:22-slim\nWORKDIR /app\nENV NODE_ENV=production\nCOPY --from=build --chown=node:node /app/node_modules ./node_modules\nCOPY --from=build --chown=node:node /app/dist ./dist\nUSER node\nEXPOSE 3000\nCMD [\"node\", \"dist/server.js\"]\n```\n\n## Gotchas\n- Never `COPY .env` or bake API keys into an image. Anyone with the image can read them. Pass secrets at run time.\n- Copying all source before installing dependencies breaks caching, so every build reinstalls.\n- `latest` tags change under you. Pin versions.\n- Alpine images are small but can break native modules. Slim images are the safer default.\n\n## Portability\nWorks with any language: swap the install and build commands for pip, Go or Maven. If you use Podman instead of Docker, the same Dockerfile works.\n\n## Example prompt\n\"Write a production Dockerfile for my Node API that is small, runs as non-root and keeps secrets out of the image.\"\n\n## Done when\n- The image builds and the app runs with `docker run`.\n- It runs as a non-root user and contains no `.env` file or keys.\n- The final image is far smaller than a single-stage build.\n\n---\nAdapted from \"multi-stage-dockerfile\" in github/awesome-copilot (MIT, Copyright GitHub, Inc.). See LICENSE.txt.","files":["LICENSE.txt"],"license":"MIT","license_name":"MIT License","license_url":"https://opensource.org/license/mit","attribution":"Based on work by GitHub awesome-copilot contributors (multi-stage-dockerfile) (Copyright GitHub, Inc., MIT License), https://github.com/github/awesome-copilot/tree/5269f7ea2d6c3a9bfda5ed878b6726cf509121fd/skills/multi-stage-dockerfile; adapted by TheSkillVault, https://theskillvault.ai","install":{"claude_code":"curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/containerise-app-with-docker/raw' -o ~/'.claude/skills/containerise-app-with-docker/SKILL.md' && curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/containerise-app-with-docker/files/LICENSE.txt' -o ~/'.claude/skills/containerise-app-with-docker/LICENSE.txt'","cursor":"curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/containerise-app-with-docker/raw' -o '.cursor/skills/containerise-app-with-docker/SKILL.md' && curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/containerise-app-with-docker/files/LICENSE.txt' -o '.cursor/skills/containerise-app-with-docker/LICENSE.txt'","agents":"curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/containerise-app-with-docker/raw' -o '.agents/skills/containerise-app-with-docker/SKILL.md' && curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/containerise-app-with-docker/files/LICENSE.txt' -o '.agents/skills/containerise-app-with-docker/LICENSE.txt'","mcp_url":"https://theskillvault.ai/mcp","cursor_mcp_deeplink":"cursor://anysphere.cursor-deeplink/mcp/install?name=theskillvault&config=eyJ1cmwiOiJodHRwczovL3RoZXNraWxsdmF1bHQuYWkvbWNwIn0%3D","mcp_config":{"mcpServers":{"theskillvault":{"url":"https://theskillvault.ai/mcp"}}}},"bundles":[],"tested_on":[{"family":"chatgpt","name":"ChatGPT","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"claude","name":"Claude","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"gemini","name":"Gemini","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"grok","name":"Grok","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"llama","name":"Llama","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"deepseek","name":"DeepSeek","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"mistral","name":"Mistral","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"qwen","name":"Qwen","outcome":null,"date":null,"source":null,"counts":false,"stale":false},{"family":"kimi","name":"Kimi K","outcome":null,"date":null,"source":null,"counts":false,"stale":false}],"proven_rule":{"threshold":5,"window_days":365,"computed_at":"2026-11-02T03:20:33.817Z"}}