Docker for Web Developers

How to setup an Apache, PHP, and HTTPS development environment with Docker

2,700 words, 14-minute read

PHP may not be the trendiest technology but it’s used by many developers and projects. According to W3Techs in December 2020, PHP is used on 79% of all websites. That estimate may be unrealistic since sites may not – and ideally shouldn’t – announce their stack. However, a more reliable statistic is that WordPress powers 40% of the web and the CMS runs on PHP.

I rarely embark on new PHP projects but have many legacy sites and apps with folders full of .php files. Installing PHP can be time-consuming and error prone. There are various versions and you’ll encounter further complexities when integrating PHP with a web server such as Apache to match a real hosting solutions.

Additionally, Windows users are offered a confusing array of options although the situation is about to become easier – Microsoft is dropping PHP support in Windows:

“We are not going to be supporting PHP for Windows in any capacity for version 8.0 and beyond.”
Dale Hirt, Microsoft

Someone is likely to compile Windows editions and the Windows Subsystem for Linux provides another option. However, the point remains that maintaining one or more PHP development environments can be difficult…

unless you use Docker.

Why use Docker? #

Docker is a tool that can install, configure, and manage software. It places a wrapper around executables known as a container. Containers are launched from pre-configured images which are a snapshot of an executable and its libraries.

Docker provides pre-built Apache and PHP images which can be downloaded and run on any OS where Docker is installed (see the Docker installation instructions).

The following sections describe how to prepare a Docker development environment which can execute PHP files locatd on your host PC.

Create SSL certificates #

Web apps use HTTPS to ensure communication between the client and the server is encrypted and cannot be intercepted. Google also penilizes content sites which remain on HTTP.

For local development, developers either:

  1. Use HTTP
    This means the local and production versions are different. It can be more difficult to spot problems such as linking to insecure assets.

  2. Or use a (fake) self-signed certificate
    This is closer to the production version but the browser still treats requests differently. For example, fake SSL assets are not cached.

A third lesser-known option is mkcert. This creates a new locally-trusted authority and SSL certificates. As far as the browser is concerned, the HTTPS connection is fully secure despite running on a local domain.

Configuring certificates need only be done once and creating them on your local machine will also work in Docker containers or WSL2. Follow the mkcert installation instructions then install a new local certificate authority in your browsers:

mkcert -install

Firefox requires some additional configuration:

  1. Locate the generated rootCA.pem file by entering mkcert -CAROOT in your terminal.
  2. Open Firefox’s menu and choose Options, then Privacy & Security. Scroll to the bottom and click View Certificates. Select the Authorities tab, click Import…, open the rootCA.pem file, and restart the browser.

Now create locally-trusted development certificates for your development domain:

mkcert localhost 127.0.0.1 ::1

It’s easier to use localhost, but you can create any domain name as long as it is referenced in your hosts file.

Rename the generated files:

Create a directory somewhere on your system, e.g. dockerphp, and copy the two .pem files into it.

Apache configuration #

Create a file named 000-default.conf in the same directory with the following Apache HTTP and HTTPS configuration. This sets the web to root /var/www/html and references the SSL certificates you created with mkcert:

<VirtualHost *:80>

ServerAdmin admin@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

<VirtualHost *:443>

SSLEngine on
SSLCertificateFile /etc/apache2/ssl/cert.pem
SSLCertificateKeyFile /etc/apache2/ssl/cert-key.pem

ServerAdmin admin@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

Docker configuration #

Create a file named Dockerfile in your directory and add the following content to build a PHP and Apache image. You can choose from dozens of starting images at Docker Hub but this example uses php:7.3-apache which has the latest version of PHP 7.3 on Apache 2.4:

FROM php:7.3-apache

RUN a2enmod ssl
RUN a2enmod rewrite

RUN mkdir -p /etc/apache2/ssl
COPY ./*.pem /etc/apache2/ssl/
COPY ./000-default.conf /etc/apache2/sites-available/000-default.conf

EXPOSE 80
EXPOSE 443

The Dockerfile:

  1. Enables Apache’s SSL and rewrite modules. Further modules can be enabled if necessary.
  2. Creates an /etc/apache2/ssl directory and copies the SSL .pem certificate files created above.
  3. Copies the Apache configuration file.
  4. Exposes ports 80 and 443 for HTTP and HTTPS accordingly.

Build the PHP Docker image #

Build a Docker image named php73 from your Dockerfile by navigating to the directory in a terminal and entering:

docker image build -t php73 .

(That last . period is important!)

Assuming you don’t have errors, a new Docker image will be built. Run docker image ls to see php73 in the list of images.

Launch a PHP container #

You can now start a Docker container from the php73 image. Navigate to any directory containing a PHP project and run the following docker command:

docker run \
-it --rm \
-p 8080:80 -p 443:443 \
--name php73site \
-v "$PWD":/var/www/html \
php73

The container will continue to run until it is stopped with Ctrl | Cmd + C.

Alternately, you may find it easier to launch the container with Docker Compose. Create a new docker-compose.yml file in the PHP project directory with the following content:

version: '3'
services:

php73site:
image: php73
container_name: php73site
volumes:
- ./:/var/www/html
ports:
- "8080:80"
- "443:443"

The Apache/PHP container can then be launched from that directory with:

docker-compose up

and stopped in another terminal with:

docker-compose down

Run PHP code #

The host directory where the Docker container is launched is bind-mounted into the container at the Apache /var/www/html root. The standard port 443 is available for HTTPS connections and port 8080 forwards to HTTP port 80 to avoid conflicts with applications such as Skype.

You can test PHP execution with an example index.php file:

<?php
phpinfo();

Launch it in your browser at http://localhost:8080/ or https://localhost/. The HTTPS version will use the mkcert SSL but, unlike self-signed certificates, the browser will not throw a security alert.

Dynamic Docker development #

A little knowledge of Docker is all that’s required to create a secure Apache and PHP 7.3 development environment. The benefits:

Finally, you’re not limited to PHP and Apache! Docker techniques can be applied to whatever server, language runtimes, or other software dependencies your application needs.


Do you want an easy-to-follow course which demonstrates how to use Docker and create practical web development environments on your Windows, macOS, or Linux PC?

Buy the "Docker for Web Developers" book & video course…

plus your country's sales tax where applicable