WarpBuild LogoWarpBuild Docs

PHP with Composer

Best practices for Dockerfile for PHP with Composer

🐳 Annotated Dockerfile for PHP with Composer:

# Stage 1: Composer dependencies
FROM composer:lts AS composer
 
# Set working directory
WORKDIR /app
 
# Copy only the files needed for composer installation
COPY composer.json composer.lock ./
 
# Install dependencies with Composer
# --no-dev for production, remove for development environments
# --no-interaction for CI/CD environments
# --no-progress to reduce log output
RUN --mount=type=cache,target=/tmp/composer-cache \
    composer install --no-dev --no-interaction --no-progress
 
# Stage 2: PHP application runtime
FROM php:8.3-fpm-alpine
 
# Install production dependencies and common extensions
RUN apk add --no-cache \
    icu-libs \
    libpq \
    && docker-php-ext-install \
    pdo_mysql \
    opcache
 
# Configure PHP for production
COPY docker/php/php.ini /usr/local/etc/php/conf.d/app.ini
COPY docker/php/fpm.conf /usr/local/etc/php-fpm.d/zz-app.conf
 
# Create a non-root user to run the application
RUN addgroup -g 1000 appuser && \
    adduser -u 1000 -G appuser -s /bin/sh -D appuser
 
# Set working directory
WORKDIR /var/www/html
 
# Copy application files
COPY --chown=appuser:appuser . /var/www/html/
 
# Copy Composer dependencies from the composer stage
COPY --from=composer --chown=appuser:appuser /app/vendor/ /var/www/html/vendor/
 
# Set proper permissions for storage and cache directories (for Laravel projects)
RUN if [ -d "storage" ]; then \
        chmod -R 775 storage bootstrap/cache; \
    fi
 
# Switch to non-root user
USER appuser
 
# Expose port 9000 for PHP-FPM
EXPOSE 9000
 
# Set the entrypoint
CMD ["php-fpm"]

🔍 Why these are best practices:

✅ Multi-stage builds

  • Separates dependency installation from the runtime environment.
  • Uses official Composer image for dependency management.
  • Eliminates build tools and dev dependencies from final image.

✅ Composer optimization

  • Installs production dependencies only with --no-dev.
  • Uses cache mounting for faster builds.
  • Copies only necessary files to optimize layer caching.

✅ Alpine-based image

  • Reduces final image size dramatically (from ~1GB to ~100MB).
  • Minimizes attack surface by including only necessary packages.
  • Keeps application lightweight and efficient.

✅ PHP configuration optimizations

  • Custom php.ini settings for production environment.
  • Properly configured PHP-FPM for containerized deployments.
  • Enables OPcache for better performance.

✅ Security best practices

  • Runs as a non-root user to enhance container security.
  • Sets proper file ownership and permissions.
  • Explicitly installs only required PHP extensions.

🚀 Additional Dockerfile best practices you can adopt:

Fine-tune OPcache settings

Optimize PHP performance with production-ready OPcache settings:

# Add this to your php.ini configuration
COPY docker/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini

With opcache.ini containing:

[opcache]
opcache.enable=1
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.max_accelerated_files=10000
opcache.memory_consumption=128
opcache.max_wasted_percentage=10
opcache.interned_strings_buffer=16
opcache.fast_shutdown=1

Configure for Laravel/Symfony projects

For Laravel applications, add Laravel-specific optimizations:

# In the final stage, run Laravel optimizations
RUN php artisan optimize && \
    php artisan config:cache && \
    php artisan route:cache && \
    php artisan view:cache

For Symfony:

# Symfony optimizations
RUN APP_ENV=prod APP_DEBUG=0 php bin/console cache:warmup

Add health checks

Monitor container health:

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
    CMD curl -f http://localhost:9000/ping || exit 1

Use .dockerignore

Exclude unnecessary files from your Docker build context:

.git
.github
vendor
node_modules
storage/logs/*
storage/app/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*
.env*
Dockerfile
docker-compose.yml
README.md

Environment-specific builds

Use build arguments to toggle between development and production builds:

ARG APP_ENV=production
 
# For development, include dev dependencies
RUN if [ "$APP_ENV" = "development" ]; then \
        composer install --no-interaction; \
    else \
        composer install --no-dev --no-interaction --optimize-autoloader; \
    fi

Add development tools conditionally

Include developer tools only in development images:

ARG APP_ENV=production
 
RUN if [ "$APP_ENV" = "development" ]; then \
        apk add --no-cache git zip unzip && \
        pecl install xdebug && \
        docker-php-ext-enable xdebug; \
    fi

Split web server and PHP-FPM

For production deployments, use separate containers for web server and PHP:

# Nginx configuration in a separate container
FROM nginx:alpine
 
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=app /var/www/html/public /var/www/html/public
 
EXPOSE 80

Implement proper init process

Handle signals correctly with proper init:

RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["php-fpm"]

By following these practices, you'll create Docker images for your PHP applications that are secure, efficient, and optimized for both development and production environments. These approaches help minimize build times, reduce image sizes, and provide a consistent experience across different deployment environments.

Last updated on