Contents

Docker packages an application together with its whole environment — language version, libraries, settings — into a container that runs the same way on a developer's laptop, in CI and on a production server. For a project, that means fast onboarding, predictable deployments without "it worked on my machine" and easy moves between servers and clouds.

Here is what Docker gives you, how to set it up for development and production and where you do not need it.

Which problems does Docker solve?

Docker solves the problem of differing environments: the PHP, Node.js or Python version, system libraries and settings on the server differ from the developer's, and code that worked locally breaks on deploy. A container pins the environment in code, making it identical everywhere.

What the project gets:

  • fast onboarding — a new developer starts the project with one command instead of spending a day on setup;
  • identical environments — development, tests and production run on the same image;
  • predictable deployments — a tested image goes to the server, not a pile of files;
  • easy rollback — going back a version means running the previous image;
  • portability — containers run on any server and in any cloud.

We described how fast setup speeds up newcomers in developer onboarding.

How is a Docker project structured?

A Docker project consists of a Dockerfile describing how to build the application image and a Docker Compose file describing which services run together: the app, database, cache, queues. An image is an immutable snapshot of the environment; a container is a running instance of an image.

# Build dependencies separately — the final image is smaller and safer
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist

FROM php:8.4-fpm-alpine
WORKDIR /var/www/app
RUN docker-php-ext-install pdo_pgsql opcache
COPY --from=vendor /app/vendor ./vendor
COPY . .
USER www-data
# compose.yaml — the whole development environment with one command
services:
  app:
    build: .
    volumes: ['.:/var/www/app']
    depends_on: [postgres, redis]
  postgres:
    image: postgres:18
    environment: { POSTGRES_PASSWORD: secret }
  redis:
    image: redis:8-alpine

Blue containers lined up in a row Photo: Unsplash

How does Docker for development differ from production?

Docker for development is optimised for convenience — code is mounted from disk, debugging works, developer tools are included — while production is optimised for reliability and security: a minimal image, code baked into the image, no extra tools and no running as root.

Aspect Development Production
Code Mounted from disk, changes visible immediately Copied into the image at build time
Dependencies Including development tools Only what is needed to run
Debugging Enabled Disabled
User Convenient for working with files Unprivileged, not root
Secrets Test values in .env From a secrets store or server environment variables
Database In a container alongside Often a managed cloud service

Rules for production images

  1. Multi-stage builds — build tools never reach the final image.
  2. Pinned base image versions, not latest.
  3. Regular rebuilds on updated base images — they carry security fixes.
  4. Secrets are never written into the image.
  5. Image vulnerability scanning in CI.

Where should containers run in production?

Containers can run on a plain server with Docker Compose, on a cloud's managed container service or in Kubernetes. For most small and medium projects the first two options are enough.

How to choose:

  • one or two servers with Docker Compose — simple, cheap and sufficient for many projects;
  • managed cloud container services — scaling and restarts without running your own orchestration;
  • Kubernetes — when there are many services, you need autoscaling and have a team to operate it.

In production the database often lives outside containers, in a managed cloud service with backups and replicas. The monolith-versus-services decision also affects how many containers you will need — see monolith vs microservices.

When is Docker unnecessary?

Docker is unnecessary for a static site on shared hosting or a site builder, or for an application that runs entirely on a platform that manages the environment itself. It can also be overkill for a very small solo project with no plans to grow.

But for any project that several people will develop or that will move between servers, Docker almost always pays for itself within the first months.

In short

  • Docker packages an app with its environment and makes development, tests and production identical.
  • A Dockerfile defines the image; Docker Compose defines the project's set of services.
  • Development optimises for convenience; production for a minimal, secure image with pinned versions.
  • Most projects are fine with Docker Compose or managed cloud services; Kubernetes is for many services.
  • Docker pays off in any project with more than one developer.

FAQ

  • Can the production database run in Docker?

    Yes, if data lives on a persistent volume and backups and monitoring are in place. But for many projects a managed cloud database with automatic backups and replicas is simpler and more reliable.

  • Does Docker slow the application down?

    On Linux servers container overhead is minimal. On macOS and Windows, file operations in mounted folders can be slower during development — mount settings address this.

  • How is Docker different from a virtual machine?

    A virtual machine emulates an entire computer with its own operating system, while a container shares the host's kernel and isolates only processes and files. That is why containers are lighter and start in seconds.