How to use docker to deploy PHP development environment


This paper mainly introduces how to use docker to build PHP development environment. The author also discusses the advantages and disadvantages of using single container or multiple containers to build docker based development environment. Recommended for PHP developers. I hope it can help you.


Environment deployment has always been a big problem, whether it is the development environment or the production environment. However, Docker packs the development environment and the production environment in a lightweight way, providing a consistent environment. It greatly improves the consistency of development and deployment. Of course, the actual situation is not so simple, because the configuration of the production environment and the development environment is completely different, for example, the log and other issues need to be configured separately, but at least it is simpler and more convenient than before. Here, take PHP development as an example to explain how docker arranges the development environment.

In general, a PHP project requires the following tools:

  1. Web server: nginx / Tengine
  2. Web program: php-fpm
  3. Database: MySQL / PostgreSQL
  4. Cache service: redis / Memcache

This is the simplest architecture. In the early days of docker’s development, docker was abused. For example, when multiple services were started in one image, the log collection is still based on syslog or other old methods. The image capacity is very large, and the basic image can reach 80m, which is totally different from the idea put forward by docker at the beginning, while Alpine Linux As a lightweight linux environment, the distribution is very suitable for the basic image of docker. Docker officials also recommend using Alpine instead of Debian as the basic image. In the future, a large number of existing official images will also be migrated to alpine. All images in this article will be based on alpine.


In this part, the author has explained the docker practice of Tengine in the nginx practice of docker container, and given the dockerfile. Because Tengine is preferred, and the official has given the alpine image of nginx, Tengine is used here. The author has uploaded the image to the official dockerhub through

docker pull chasontang/tengine:2.1.2_f

To obtain the image, see dockerfile.


Docker has officially provided the 7.0.7-fpm-alpine image of PHP. The dockerfile is as follows:

FROM alpine:3.4


# persistent / runtime deps


    autoconf \

    file \

    g++ \

    gcc \

    libc-dev \

    make \

    pkgconf \


RUN apk add --no-cache --virtual .persistent-deps \

    ca-certificates \



# ensure www-data user exists

RUN set -x \

  && addgroup -g 82 -S www-data \

  && adduser -u 82 -D -S -G www-data www-data

# 82 is the standard uid/gid for "www-data" in Alpine





ENV PHP_INI_DIR /usr/local/etc/php

RUN mkdir -p $PHP_INI_DIR/conf.d



ENV PHP_EXTRA_CONFIGURE_ARGS --enable-fpm --with-fpm-user=www-data --with-fpm-group=www-data






ENV PHP_FILENAME php-7.0.7.tar.xz

ENV PHP_SHA256 9cc64a7459242c79c10e79d74feaf5bae3541f604966ceb600c3d2e8f5fe4794


RUN set -xe \

  && apk add --no-cache --virtual .build-deps \


    curl-dev \

    gnupg \

    libedit-dev \

    libxml2-dev \

    openssl-dev \

    sqlite-dev \

  && curl -fSL "$PHP_FILENAME/from/this/mirror" -o "$PHP_FILENAME" \

  && echo "$PHP_SHA256 *$PHP_FILENAME" | sha256sum -c - \

  && curl -fSL "$PHP_FILENAME.asc/from/this/mirror" -o "$PHP_FILENAME.asc" \

  && export GNUPGHOME="$(mktemp -d)" \

  && for key in $GPG_KEYS; do \

    gpg --keyserver --recv-keys "$key"; \

  done \

  && gpg --batch --verify "$PHP_FILENAME.asc" "$PHP_FILENAME" \

  && rm -r "$GNUPGHOME" "$PHP_FILENAME.asc" \

  && mkdir -p /usr/src \

  && tar -Jxf "$PHP_FILENAME" -C /usr/src \

  && mv "/usr/src/php-$PHP_VERSION" /usr/src/php \

  && rm "$PHP_FILENAME" \

  && cd /usr/src/php \

  && ./configure \

    --with-config-file-path="$PHP_INI_DIR" \

    --with-config-file-scan-dir="$PHP_INI_DIR/conf.d" \


    --disable-cgi \

# --enable-mysqlnd is included here because it's harder to compile after the fact than extensions are (since it's a plugin for several extensions, not an extension in itself)

    --enable-mysqlnd \

# --enable-mbstring is included here because otherwise there's no way to get pecl to use it properly (see

    --enable-mbstring \

    --with-curl \

    --with-libedit \

    --with-openssl \

    --with-zlib \

  && make -j"$(getconf _NPROCESSORS_ONLN)" \

  && make install \

  && { find /usr/local/bin /usr/local/sbin -type f -perm +0111 -exec strip --strip-all '{}' + || true; } \

  && make clean \

  && runDeps="$( \

    scanelf --needed --nobanner --recursive /usr/local \

      | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \

      | sort -u \

      | xargs -r apk info --installed \

      | sort -u \

  )" \

  && apk add --no-cache --virtual .php-rundeps $runDeps \

  && apk del .build-deps


COPY docker-php-ext-* /usr/local/bin/



WORKDIR /var/www/html


RUN set -ex \

  && cd /usr/local/etc \

  && if [ -d php-fpm.d ]; then \

    # for some reason, upstream's php-fpm.conf.default has "include=NONE/etc/php-fpm.d/*.conf"

    sed 's!=NONE/!=!g' php-fpm.conf.default | tee php-fpm.conf > /dev/null; \

    cp php-fpm.d/www.conf.default php-fpm.d/www.conf; \

  else \

    # PHP 5.x don't use "include=" by default, so we'll create our own simple config that mimics PHP 7+ for consistency

    mkdir php-fpm.d; \

    cp php-fpm.conf.default php-fpm.d/www.conf; \

    { \

      echo '[global]'; \

      echo 'include=etc/php-fpm.d/*.conf'; \

    } | tee php-fpm.conf; \

  fi \

  && { \

    echo '[global]'; \

    echo 'error_log = /proc/self/fd/2'; \

    echo; \

    echo '[www]'; \

    echo '; if we send this to /proc/self/fd/1, it never appears'; \

    echo 'access.log = /proc/self/fd/2'; \

    echo; \

    echo 'clear_env = no'; \

    echo; \

    echo '; Ensure worker stdout and stderr are sent to the main error log.'; \

    echo 'catch_workers_output = yes'; \

  } | tee php-fpm.d/docker.conf \

  && { \

    echo '[global]'; \

    echo 'daemonize = no'; \

    echo; \

    echo '[www]'; \

    echo 'listen = [::]:9000'; \

  } | tee php-fpm.d/zz-docker.conf



CMD ["php-fpm"]


First, the image is inherited from alpine:3.4 Image, use APK command to install PHP minimal dependency, and add www data as the running user of PHP FPM. Specify PHP configuration file to / usr / local / etc / PHP, then download PHP SRC, compile and install. Here, you can refer to the PHP compilation and installation article written by the author before. The parameters are all right. The installation directory is specified to / usr / local, and then use scanelf to get the list of dependent runtime libraries, and remove other installation packages. Copy docker PHP ext configure, docker PHP ext enable, and docker PHP ext install to the container. These three files are used for subsequent installation extension. Then PHP- fpm.conf Copy to the configuration directory, error_ Log and access_ Log is specified to the terminal standard output, and daemonize = no indicates that it does not run as a service process. The expose 9000 port is used to communicate with other containers, and then CMD [“PHP FPM”] runs PHP FPM. And the working directory is assigned to / var / www / HTML.


After the basic image has been completed, we can use the basic image to configure the container, but it will be very troublesome to start the container with the manual docker command. But fortunately, the official has provided the docker compose command to arrange containers. Only one docker needs to be written- compose.yaml Documents are enough. Please refer to official documents for details.

version: '2'



  image: php:7.0.7-fpm-alpine


   - "./src:/var/www/html"

  restart: always




   - php-fpm


   - php-fpm

  image: chasontang/tengine:2.1.2_f


   - "./nginx.vh.default.conf:/etc/nginx/conf.d/default.conf"


   - "80:80"

  restart: always

It is easy to understand that there are two services defined here, PHP FPM dependency php:7.0.7-fpm-alpine Image, and map SRC folder to / var / www / HTML folder. Tengine service relies on PHP FPM service, and link PHP FPM service, so it can communicate with PHP FPM container through network. Tengine service is based on chasontang/ tengine:2.1.2_ F mirror, and nginx.vh.default The. Conf file is mapped to / etc / nginx / conf.d/ default.conf Documents. Then let’s see nginx.vh.default .conf

server {

  listen    80;

  server_name localhost;


  #charset koi8-r;


  #access_log logs/host.access.log main;


  location / {

    root  html;

    index index.html index.htm;



  #error_page 404       /404.html;


  # redirect server error pages to the static page /50x.html


  error_page  500 502 503 504 /50x.html;

  location = /50x.html {

    root  html;



  # proxy the PHP scripts to Apache listening on


  #location ~ \.php$ {

  #  proxy_pass;



  location ~ [^/]\.php(/|$) {

    fastcgi_split_path_info ^(.+?\.php)(/.*)$;

    fastcgi_pass php-fpm:9000;

    fastcgi_index index.php;

    fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;

    fastcgi_param PATH_INFO $fastcgi_path_info;

    include fastcgi_params;



  # deny access to .htaccess files, if Apache's document root

  # concurs with nginx's one


  #location ~ /\.ht {

  #  deny all;



Because / etc / nginx/ nginx.conf Include / etc / nginx / conf.d / *. Conf; contains this directory, that is, you can use your own nginx virtual host configuration instead of the default virtual host configuration, or add virtual host configuration.

As you can see from above, default.conf The file defines a location to match the URL containing. PHP, and then splits it out of the path_ Info parameter, passing these variables to PHP- fpm:9000 PHP FPM service.

Note that because nginx and PHP FPM are not on the same host, nginx only processes static files and routes them. The actual PHP files are executed in the PHP FPM container. So script_ The filename variable must use the directory in the PHP FPM container, so it’s hard coded here. Of course, two containers can also share the same data volume, but the author thinks that this is just for convenience of container arrangement, and there is no other advantage.

It’s easy! Now we can quickly start and update the environment.

This is how to use docker to deploy PHP development environment

I hope that the above content can help you. Many PHPer will encounter some problems and bottlenecks when they are advanced. There is no sense of direction when they write too much business code. I don’t know where to start to improve. I have collated some information about this, including but not limited to: distributed architecture, high scalability, high performance, high concurrency, server performance tuning, tp6, laravel, yii2, redis, Swoo For advanced advanced dry goods of multiple knowledge points, such as Le, swoft, Kafka, MySQL optimization, shell script, docker, microservice, nginx, etc., you can share what you need for free, and you can join my official group to click here.

Recommended Today

What are the new methods of visual + map technology?

Last week, Ren Xiaofeng, chief scientist of Alibaba Gaode map, made a technical exchange with you on the development of computer vision related technology and the application in the field of map travel at the online live broadcast activity of “cloud dialogue” between senior students of Alibaba. The interaction between live broadcast is hot. Especially […]