DEV Community

Cover image for Some Best Practices for Writing Dockerfiles
MD ARIFUL HAQUE
MD ARIFUL HAQUE

Posted on

Some Best Practices for Writing Dockerfiles

When writing Dockerfiles for PHP and MySQL, it's important to focus on security, efficiency, and maintainability. Here are some best practices specific to Dockerfiles for PHP and MySQL environments:

1. Use Official Base Images

  • Why: Official Docker images for PHP and MySQL are regularly updated and optimized for performance and security. Always use these as your base.
  • Example:

     FROM php:7.4-fpm
     FROM mysql:8.0
    

2. Minimize PHP Extensions and Packages

  • Why: Install only the necessary PHP extensions and Linux packages to keep your image lightweight and reduce potential security vulnerabilities.
  • Example:

     RUN apt-get update && apt-get install -y \
         libpng-dev \
         libjpeg-dev \
         libfreetype6-dev \
     && docker-php-ext-configure gd --with-freetype --with-jpeg \
     && docker-php-ext-install gd
    

3. Optimize PHP Configuration

  • Why: Customize your PHP configuration to suit your application’s needs. Use environment variables or copy custom php.ini files to configure settings like memory limits, error reporting, and upload sizes.
  • Example:

     COPY php.ini /usr/local/etc/php/
    

4. Leverage Multi-Stage Builds for PHP

  • Why: Use multi-stage builds to separate the build environment (e.g., compiling PHP extensions) from the final runtime environment. This keeps the final image lean and secure.
  • Example:

     FROM php:7.4-fpm AS builder
     WORKDIR /usr/src/php/ext
     RUN docker-php-ext-install pdo_mysql
    
     FROM php:7.4-fpm
     COPY --from=builder /usr/local/lib/php/extensions/no-debug-non-zts-20190902/pdo_mysql.so /usr/local/lib/php/extensions/no-debug-non-zts-20190902/
     RUN docker-php-ext-enable pdo_mysql
    

5. Configure MySQL for Production

  • Why: Use environment variables to configure MySQL in a way that’s optimized for production. Avoid hardcoding credentials and use Docker secrets or environment variables instead.
  • Example:

     ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
     ENV MYSQL_DATABASE=mydatabase
     ENV MYSQL_USER=myuser
     ENV MYSQL_PASSWORD=${MYSQL_PASSWORD}
    

6. Persist MySQL Data Using Volumes

  • Why: To avoid data loss, persist MySQL data outside the container using Docker volumes. This ensures your data is safe even if the container is removed.
  • Example:

     version: '3.8'
     services:
       db:
         image: mysql:8.0
         volumes:
           - db_data:/var/lib/mysql
         environment:
           MYSQL_ROOT_PASSWORD: example
           MYSQL_DATABASE: exampledb
    
     volumes:
       db_data:
    

7. Use .dockerignore for PHP Projects

  • Why: Exclude unnecessary files and directories (e.g., node_modules, .git, tests, etc.) from being copied into the Docker image, reducing the build context and resulting image size.
  • Example:

     .git
     node_modules
     tests
     .env
    

8. Keep the Image Lean

  • Why: Remove unnecessary dependencies and temporary files after installation to minimize the image size.
  • Example:

     RUN apt-get update && apt-get install -y \
         libzip-dev \
     && docker-php-ext-install zip \
     && apt-get clean \
     && rm -rf /var/lib/apt/lists/*
    

9. Use Supervisord for Managing Multiple Processes

  • Why: If your PHP container needs to run multiple services (e.g., PHP-FPM and a cron job), use supervisord to manage them efficiently.
  • Example:

     RUN apt-get update && apt-get install -y supervisor
     COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
     CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
    

10. Enable OPCache for PHP

  • Why: Enabling OPCache improves PHP performance by caching the compiled bytecode of PHP scripts, reducing the need for PHP to recompile the scripts on each request.
  • Example:

     RUN docker-php-ext-install opcache
     COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini
    

11. Separate Application Code and Configuration

  • Why: Keep your application code and configuration files (like php.ini or my.cnf) separate to allow easy updates and better management.
  • Example:

     COPY src/ /var/www/html/
     COPY php.ini /usr/local/etc/php/
    

12. Health Checks

  • Why: Use Docker's health check functionality to monitor the health of your PHP and MySQL containers, ensuring they are running correctly.
  • Example:

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

Conclusion

By following these best practices, you can create Dockerfiles that produce efficient, secure, and maintainable containers for your PHP and MySQL projects. This will result in a smoother development process and more stable production environments.

Top comments (1)

Collapse
 
lupusmichaelis profile image
Mickaël Wolff

There are some bad practices in your list :-/ Allow me to lean in:

  1. this is fine as a default, but if they don't fit the use case, you shouldn't hesitate to swap them for specialised or home made
  2. ok, but do it properly. For example, with Debian, I use some script to embed the logic to ensure the footprint's minimal: initialization. And you shouldn't chain operations that have no immediate logic. In your example, adding a module would force apt-get to run.
  3. Okish
  4. Ok
  5. Yes, but why default values for the database space and the database user? No default at all. I would add also to provide a way to warn the client of the image that the environment isn't properly configured
  6. First and foremost: please stop with the version key, it's been deprecated for years. As for using DB inside a container, this is contencious. Many Ops will scream in horror due to the inefficiency and how unsafe it is to manage it. In a dev environement it's ok; in production, shouldn't happen. And the volume doesn't guarantee persistency. Anyone who fought with a problematic compose deployment knows how quick it is to docker compose down -v :o)
  7. Yes, but not only for size: it's mostly a security concern. Don't forget to add Dockerfile in this.
  8. See 2. ;)
  9. No. NO NO NO NO NOOOOOOOOOOOO!!!! Please no (insert Michael Scott mème here). It's a bad practice as per official guidelines: you want your app to be PID 1 in order to properly handle signals, scale, etc. If you want cron stuff, let an other container take that responsibility.
  10. Shouldn't be the default. Should be put in place if the need arises and with proper monitoring and cache invalidation procedures.
  11. well, I guess your example doesn't match the intent, as you show PHP tweaking configuration rather than the app's configuration. As much as I do agree with that, unfortunately, most PHP apps and PHP framework will embed the actual app configuration.
  12. I do concur, but why exit 1?

(If you're interested in sources of my claims, I'll provide)