"It works on my machine" has cost every team hours. Docker fixes much of it by packaging an app with its runtime and dependencies, so it runs the same on a laptop, a CI server and production. You do not need to master everything to benefit. This starter covers what a web developer uses every week.
The three ideas to know
- Image: a read-only template that contains your app, its runtime and its dependencies.
- Container: a running instance of an image. You can start many from one image.
- Registry: a place to store and share images, such as Docker Hub, GitHub Container Registry or AWS ECR.
A first Dockerfile for a Node.js app
A Dockerfile lists the steps to build an image. A typical Node setup looks like this:
FROM node:22-alpinepicks a small base image.WORKDIR /appsets the working folder.COPY package*.json ./andRUN npm ciinstall dependencies.COPY . .copies the source.CMD ["npm", "start"]starts the app.
Build it with docker build -t my-app . and run it with docker run -p 3000:3000 my-app.
Order your layers for fast builds
Docker caches each step. Copy package*.json and install dependencies before copying the rest of the source. Then a code change reuses the cached dependency layer and rebuilds in seconds instead of minutes. Add a .dockerignore file with node_modules, .git and local env files so they never enter the image.
Use multi-stage builds for small images
Build in one stage and copy only the output into a clean runtime stage. The final image holds your built app and production dependencies, not compilers and dev tools. Smaller images pull faster, start faster and expose less to attackers.
Docker Compose for the whole stack
Most apps need a database or a cache too. Compose describes all services in one compose.yaml and starts them together with docker compose up. A web app, PostgreSQL and Redis become one command and one file. New teammates clone the repository, run that command and have a working environment in minutes.
- Use named volumes so database data survives container restarts.
- Use
depends_onwith health checks so the app waits for the database. - Mount your source folder as a volume in development for live reload.
Handle configuration and secrets
- Pass settings through environment variables, not hardcoded values.
- Keep
.envfiles out of Git and out of the image. - Use your platform's secret store in production.
- Never bake API keys into a Dockerfile, because they remain in image history.
Security basics
- Run as a non-root user with the
USERinstruction. - Pin base image versions instead of using
latest. - Scan images for known vulnerabilities in CI.
- Rebuild regularly to pick up patched base images.
Common beginner problems
- Port not reachable: the app must listen on
0.0.0.0inside the container, notlocalhost. - Changes not appearing: rebuild the image or mount the source as a volume.
- Disk filling up: run
docker system pruneoccasionally to remove unused images and containers. - Data lost: store anything you need to keep in a volume, because container storage disappears with the container.
From laptop to production
Build the image in CI, tag it with the commit hash, push it to a registry and deploy that exact image. The same artifact moves through staging and production, which removes a whole class of environment bugs. This is the base of the pipeline described in Automating Deployments with CI/CD, and the next step toward orchestration in Kubernetes for Beginners.
A realistic Compose example
A typical stack for a business app has four containers: a Next.js front end, an API, a PostgreSQL database and Redis for queues and caching. With Compose, a new developer clones the repository, copies the example environment file and runs one command. The database starts with seed data, the API waits until it is healthy, and the web app reloads when files change. Onboarding drops from a day of installing tools to a few minutes.
Checklist before you deploy a container
- Health check endpoint that returns quickly.
- Graceful shutdown so requests finish before the container stops.
- Logs written to standard output, not to files inside the container.
- Resource limits set so one service cannot starve the others.
- Database migrations run as a separate, controlled step.
Containers behind our own products
Packaging makes it easier to run several environments of the same product, such as local, development and production, with the same image and different settings. That is how we keep releases predictable for software like the Lena Dena business platform. Background workers, including the ones that send WhatsApp messages and due-date reminders in our WhatsApp automation, run as their own containers, so a slow job never blocks the web app.
We containerize and deploy web applications for clients. See our cloud and DevOps services or get in touch to simplify your setup.

