DevOps

Docker Compose — a PHP development environment from scratch

Piotr Wiśniewski 03.10.2026 382 wyświetleń 3 komentarzy
This translation was generated by AI and may contain inaccuracies. Read the original (PL)

Why Docker?

"Works on my machine" is history. Docker guarantees that the dev environment = staging environment = production.

Benefits:

  • isolation — each project has its own versions of PHP, PostgreSQL, Node
  • onboarding — a new developer: git clone + docker compose up -d = ready
  • reproducibility — always the same result, regardless of the machine

The compose.yaml file

services:

  ###> php-fpm ###
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    volumes:
      - .:/var/www/html:cached
    depends_on:
      database:
        condition: service_healthy
    environment:
      APP_ENV: dev

  ###> nginx ###
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html:ro
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app

  ###> postgresql ###
  database:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB:       ${POSTGRES_DB:-app}
      POSTGRES_USER:     ${POSTGRES_USER:-app}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret}
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app}"]
      interval: 10s
      timeout: 5s
      retries: 5

  ###> mailpit (captures emails in dev) ###
  mailer:
    image: axllent/mailpit:latest
    ports:
      - "8025:8025"   # UI
      - "1025:1025"   # SMTP

volumes:
  db_data:

Dockerfile for PHP

FROM php:8.3-fpm-alpine

RUN apk add --no-cache \
        git \
        unzip \
        libpq-dev \
    && docker-php-ext-install \
        pdo pdo_pgsql \
        opcache

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

COPY . .

RUN composer install --no-dev --optimize-autoloader

Everyday commands

# start in the background
docker compose up -d

# stop
docker compose down

# logs (live)
docker compose logs -f app

# enter the container
docker compose exec app bash

# run Symfony commands
docker compose exec app php bin/console cache:clear
docker compose exec app php bin/console doctrine:migrations:migrate

# rebuild after changing the Dockerfile
docker compose build --no-cache app

Nginx — configuration for Symfony

server {
    listen 80;
    root /var/www/html/public;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass app:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        internal;
    }

    location ~ \.php$ {
        return 404;
    }
}

Healthcheck — what for?

Without a healthcheck, containers may start in the wrong order:

app starts → tries to connect to the database → database not ready yet → error!

With condition: service_healthy in depends_on, app will wait for database. Always add a healthcheck to the database.

Tagi: #PHP #Docker #Linux
Udostępnij: Facebook X LinkedIn

Komentarze (3)


A
Anna Nowak 21.10.2026, 14:27

Healthcheck dla bazy to must-have. Bez niego migracje startują za wcześnie.

J
Jan Kowalski 21.10.2026, 14:27

Jak masz zoptymalizowany Dockerfile? Mój rebuild po każdej zmianie trwa 3 min.

P
Piotr Wiśniewski 21.10.2026, 14:27

Przenieś COPY composer.json przed RUN composer install — cache warstw pomoże.

Zaloguj się, aby dodać komentarz.