🐳 How to Run Any Project in Docker: A Complete Guide This article provides a comprehensive guide to using Docker, explaining how it packages applications and dependencies into portable containers to eliminate environment-specific issues. It covers essential steps like writing a Dockerfile, building and running containers, and using Docker Compose to manage multi-service applications with databases and caches. The guide also includes best practices for layer caching, environment variables, and separating development and production configurations. From zero to containerized in minutes — no "works on my machine" excuses Why Docker? You've probably heard it before: "It works on my machine." Docker exists to make that phrase obsolete. Docker lets you package your application and all its dependencies — runtimes, libraries, config files — into a single, portable unit called a container . That container runs identically on your laptop, your teammate's Windows machine, a CI server, or a cloud VM. Before we dive in, here's the quick mental model: - Image → A blueprint like a class in OOP - Container → A running instance of an image like an object - Dockerfile → The recipe for building an image - Docker Compose → A tool to orchestrate multiple containers together Prerequisites - Docker Desktop https://www.docker.com/products/docker-desktop/ installed includes Docker Compose - Basic terminal familiarity - A project to containerize we'll use examples for Node.js, Python, and a generic approach Verify your install: docker --version Docker version 26.x.x docker compose version Docker Compose version v2.x.x Part 1: The Anatomy of a Dockerfile A Dockerfile is a plain text file with instructions Docker reads top-to-bottom to build your image. 1. Base image — what you're building ON TOP OF FROM node:20-alpine 2. Set the working directory inside the container WORKDIR /app 3. Copy dependency files first for layer caching COPY package .json ./ 4. Install dependencies RUN npm install 5. Copy the rest of your source code COPY . . 6. Expose the port your app listens on EXPOSE 3000 7. The command to run when the container starts CMD "node", "server.js" Key Instructions Explained | Instruction | Purpose | |---|---| FROM | Sets the base image. Always the first instruction. | WORKDIR | Sets the working directory for subsequent commands. Created if it doesn't exist. | COPY | Copies files from your host into the image. | RUN | Executes a command during the build phase installs packages, compiles code . | ENV | Sets environment variables available at runtime. | EXPOSE | Documents which port the app uses informational; doesn't actually publish . | CMD | The default command when the container starts. Only one per Dockerfile. | ENTRYPOINT | Like CMD, but harder to override — use for "always run this". | Pro tip:Order your Dockerfile from least-to-most frequently changed. Docker caches each layer, so stable layers like installing dependencies won't re-run unless they change. Part 2: Dockerizing a Node.js Project Project structure my-app/ ├── src/ │ └── index.js ├── package.json ├── package-lock.json └── Dockerfile Dockerfile FROM node:20-alpine WORKDIR /app Copy lockfile and package.json first for cache efficiency COPY package .json ./ RUN npm ci --only=production COPY src/ ./src/ EXPOSE 3000 CMD "node", "src/index.js" Build and run Build the image and tag it docker build -t my-node-app . Run it, mapping host port 8080 → container port 3000 docker run -p 8080:3000 my-node-app Visit http://localhost:8080 — your app is running inside Docker. Part 3: Dockerizing a Python Project FROM python:3.12-slim WORKDIR /app Install dependencies COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD "python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000" Note the: By default, many dev servers bind to --host 0.0.0.0 127.0.0.1 localhost inside the container . You must bind to 0.0.0.0 to accept connections from outside the container. Part 4: Docker Compose — Running Multiple Services Real projects rarely have just one service. You need a database, a cache, maybe a background worker. Docker Compose lets you define and run all of them together. Example: Node.js app + PostgreSQL + Redis docker-compose.yml services: app: build: . ports: - "3000:3000" environment: - DATABASE URL=postgres://user:password@db:5432/mydb - REDIS URL=redis://cache:6379 depends on: db: condition: service healthy cache: condition: service started volumes: - .:/app Mount source code for hot reload - /app/node modules Prevent host node modules from overwriting db: image: postgres:16-alpine environment: POSTGRES USER: user POSTGRES PASSWORD: password POSTGRES DB: mydb volumes: - postgres data:/var/lib/postgresql/data healthcheck: test: "CMD-SHELL", "pg isready -U user -d mydb" interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine ports: - "6379:6379" volumes: postgres data: Run everything with one command Start all services in the background docker compose up -d View logs docker compose logs -f app Stop everything docker compose down Stop and remove volumes wipes database data docker compose down -v Part 5: Environment Variables & Secrets Never hardcode secrets in your Dockerfile or Compose file. Use a .env file: .env add this to .gitignore POSTGRES PASSWORD=supersecret API KEY=abc123 Docker Compose automatically picks up .env in the same directory: services: app: environment: - API KEY=${API KEY} db: environment: - POSTGRES PASSWORD=${POSTGRES PASSWORD} For production, use Docker Secrets, Vault, AWS Secrets Manager, or your platform's secret management. Part 6: Development vs Production Configurations Use multiple Compose files to separate concerns: my-app/ ├── docker-compose.yml Base config ├── docker-compose.dev.yml Dev overrides hot reload, debug ports └── docker-compose.prod.yml Prod overrides replicas, logging docker-compose.dev.yml — adds hot reload: services: app: volumes: - .:/app command: npm run dev environment: - NODE ENV=development docker-compose.prod.yml — tightens things up: services: app: restart: always environment: - NODE ENV=production deploy: replicas: 2 Run with merged configs: Development docker compose -f docker-compose.yml -f docker-compose.dev.yml up Production docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d Part 7: Useful Docker Commands Cheat Sheet Images docker images List all local images docker pull nginx:alpine Pull image from Docker Hub docker rmi my-app Remove an image docker image prune Remove unused images Containers docker ps List running containers docker ps -a List all containers including stopped docker stop