Preskočiť na obsah
DevOps Backend 23. jún 2026 · 11 min čítania

Docker pre PHP vývojárov: od lokálneho vývoja po produkciu

„Na mojom počítači to funguje." Táto veta sa v tíme s Dockerom nevysloví. PHP 8.4 + Nginx + MariaDB + Redis v identickom prostredí — lokálne aj na VPS. Tento návod vás prevedie kompletným stackom od docker-compose.yml po produkčný multi-stage build.

DC

Dušan Chlpek

Senior PHP vývojár · GEAR s.r.o. · 25+ rokov praxe

Prečo Docker namiesto XAMPP alebo lokálneho PHP

XAMPP a Laragon sú skvelé na rýchly štart. Ale pri produkčnom projekte narazíte na problém: dev prostredie sa odlišuje od servera. Iná verzia PHP, iná konfigurácia MySQL, chýbajúce rozšírenia. Docker tento rozdiel eliminuje.

S Dockerom definujete celý stack v jednom súbore. Každý vývojár, CI/CD pipeline aj produkčný server spúšťajú identický kontajner. Výsledok: menej „works on my machine" bugov a reprodukovateľné deploymenty.

Predpoklady: Docker Desktop (Windows/Mac) alebo Docker Engine (Linux) nainštalovaný. Príklady sú testované s Docker 26.x a Compose v2.

Štruktúra projektu

Pre Laravel alebo vlastnú PHP aplikáciu odporúčam túto adresárovú štruktúru:

projekt/
├── docker/
│   ├── nginx/
│   │   └── default.conf
│   └── php/
│       └── local.ini
├── src/                  ← váš PHP kód (Laravel, atď.)
├── Dockerfile
├── docker-compose.yml
├── docker-compose.prod.yml
└── .env

docker-compose.yml pre lokálny vývoj

Toto je základ pre lokálne prostredie. Všetky zmeny kódu sa ihneď prejavia vďaka volume mountu — bez restartu kontajnera.

services:
  app:
    build:
      context: .
      target: dev
    volumes:
      - ./src:/var/www/html
      - ./docker/php/local.ini:/usr/local/etc/php/conf.d/local.ini
    depends_on:
      - db
      - redis
    networks:
      - backend

  nginx:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
    networks:
      - backend

  db:
    image: mariadb:11.4
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MARIADB_DATABASE: ${DB_DATABASE}
      MARIADB_USER: ${DB_USERNAME}
      MARIADB_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - backend

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    networks:
      - backend

volumes:
  db_data:

networks:
  backend:

Multi-stage Dockerfile

Multi-stage build je kľúčový pre produkciu. Výsledný image neobsahuje dev závislosti, čím je výrazne menší a bezpečnejší.

# Dockerfile
FROM php:8.4-fpm-alpine AS base

RUN apk add --no-cache \
    libpng-dev libjpeg-turbo-dev libwebp-dev libavif-dev \
    oniguruma-dev libzip-dev icu-dev libxml2-dev \
    && docker-php-ext-configure gd --with-jpeg --with-webp --with-avif \
    && docker-php-ext-install \
       pdo_mysql mbstring zip gd intl opcache bcmath exif

COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
RUN install-php-extensions redis

WORKDIR /var/www/html

# ─── Dev stage ──────────────────────────────────────────────
FROM base AS dev

RUN apk add --no-cache git unzip nodejs npm \
    && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

COPY docker/php/local.ini /usr/local/etc/php/conf.d/local.ini

# ─── Build stage ─────────────────────────────────────────────
FROM dev AS build

COPY src/ .
RUN composer install --no-dev --optimize-autoloader --no-interaction \
    && npm ci --prefix . \
    && npm run build --prefix . \
    && rm -rf node_modules

# ─── Production stage ────────────────────────────────────────
FROM base AS prod

COPY docker/php/prod.ini /usr/local/etc/php/conf.d/local.ini
COPY --from=build /var/www/html .

RUN chown -R www-data:www-data /var/www/html/storage \
    && chmod -R 755 /var/www/html/storage

USER www-data
Dev vs. prod PHP ini: V local.ini zapnite display_errors = On a xdebug. V prod.ini naopak: display_errors = Off, opcache.enable = 1, opcache.validate_timestamps = 0.

Nginx konfigurácia pre PHP-FPM

# docker/nginx/default.conf
server {
    listen 80;
    server_name _;
    root /var/www/html/public;
    index index.php;

    client_max_body_size 20M;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
    }

    location ~* \.(css|js|png|jpg|jpeg|gif|webp|avif|ico|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    location ~ /\.(?!well-known) {
        deny all;
    }
}

Spustenie a bežné príkazy

# Spustenie (lokálne)
docker compose up -d

# Vstup do PHP kontajnera
docker compose exec app sh

# Artisan príkazy
docker compose exec app php artisan migrate
docker compose exec app php artisan queue:work

# Composer
docker compose exec app composer install

# Logy
docker compose logs -f app
docker compose logs -f nginx
Pozor na .env: Súbor .env nikdy nepridávajte do git repozitára. Použite .env.example s prázdnymi hodnotami a .gitignore pre .env.

Produkčný docker-compose.prod.yml

Pre produkciu použite override súbor, ktorý prepíše lokálnu konfiguráciu:

# docker-compose.prod.yml
services:
  app:
    build:
      context: .
      target: prod
    restart: unless-stopped
    environment:
      APP_ENV: production
      APP_DEBUG: "false"
    volumes: []  # žiadne volume mounty — kód je v image

  nginx:
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./docker/nginx/prod.conf:/etc/nginx/conf.d/default.conf
      - /etc/letsencrypt:/etc/letsencrypt:ro
    restart: unless-stopped

Spustenie na VPS:

# Build a spustenie produkčného stacku
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

# Zero-downtime update
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app

CI/CD pipeline — GitHub Actions

Automatický deployment po push na main vetvu:

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build --target prod -t app:${{ github.sha }} .

      - name: Push to registry
        run: |
          echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker tag app:${{ github.sha }} ghcr.io/${{ github.repository }}:latest
          docker push ghcr.io/${{ github.repository }}:latest

      - name: Deploy na VPS
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_KEY }}
          script: |
            cd /var/www/projekt
            docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
            docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app
            docker compose exec -T app php artisan migrate --force

Záver: Docker = reprodukovateľnosť a spokojnosť

Docker nie je len módny nástroj. Je to riešenie reálneho problému — rozdielnych prostredí. Raz nastavený stack funguje identicky pre každého vývojára, v každom CI systéme, na každom VPS. Investícia niekoľkých hodín do správnej konfigurácie šetrí desiatky hodín debugovania „produkčných" bugov.

Odporúčam začať lokálnym docker compose up a postupne pridávať produkčné vrstvy — nie naopak.

Potrebujete nastaviť CI/CD alebo Docker pre váš PHP projekt?

Navrhnem kompletný DevOps stack — od lokálneho vývoja po automatický deployment. Odpoveď do 24 hodín v pracovný deň.

Nezáväzný dopyt

Ďalšie články

Zavolať E-mail Dopyt

Ochrana súkromia

Táto stránka využíva cookies pre nevyhnutné fungovanie. Rešpektujeme vaše súkromie a legislatívu GDPR.