Adding GD support to a Docker image

I had to create a new docker container to emulate a site we have working on older versions of MySQL and PHP (don’t ask, long story).

The CMS was failing spectacularly. After suspecting memory issues and other esoteric settings I realized it was much more basic: there was no GD support in this container and the plugin that manipulated images was dying without generating any error messages (thanks for that, plugin developer).

Long story short, here’s my Dockerfile to get GD support working. This was one of those “look at a bunch of stack exchange answers and kind of merge them together” solutions so I’m not 100% sure how universally it will work. The bits in red are what I added for GD support. Frankly I’m not sure about that RUN NUMPROC line… I need to research that more because I honestly don’t understand what it is doing.

EDIT: OK the -j parameter passed to docker-php-ext-install is the number of make jobs that can run concurrently (in order to speed up compilation). The RUN NUMPROC line is looking at how many CPUs you have and telling make it can run that many jobs at the same time.

Edit 2: As of April 2, 2019 I started running into problems where my docker build who throw up errors like this:
Failed to fetch http://deb.debian.org/debian/dists/jessie-updates/main/binary-amd64/Packages

Apparently Debian “Jessie” updates have been moved to the archives. The solution is to tweak the sources list, new line added in green
Solution comes from https://github.com/debuerreotype/docker-debian-artifacts/issues/66#issuecomment-476616579

Edit 3: When using PHP 7.4 (php:7.4-apache image specifically) the RUN docker-php-ext-configure gd line will fail. 7.4 version is in BLUE now. Use this INSTEAD of the “RUN docker-php-ext-configure gd” command in RED. You’ll note I’m only adding jpeg support; it is surmised by the Internet that png support is now baked in. I’ll update the post if I find that is not the case.

FROM php:5.6.38-apache

COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
COPY php.ini /usr/local/etc/php/php.ini
RUN mkdir -p /etc/apache2/ssl/
COPY ./ssl.crt /etc/apache2/ssl/ssl.crt
COPY ./ssl.key /etc/apache2/ssl/ssl.key
RUN mkdir -p /var/run/apache2/

RUN sed -i '/jessie-updates/d' /etc/apt/sources.list # Now archived

RUN apt-get update -y && apt-get install -y zlib1g-dev libjpeg62-turbo libpng-dev libjpeg-dev

RUN docker-php-ext-configure gd --with-jpeg

RUN docker-php-ext-configure gd \
--with-png-dir=/usr/lib/ \
--with-jpeg-dir=/usr/lib/ \
--with-gd

RUN NUMPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) \
&& docker-php-ext-install -j${NUMPROC} gd

RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install mysqli
RUN a2enmod rewrite
RUN a2enmod ssl

EXPOSE 80
EXPOSE 443

Creating a self-signed SSL certificate for local Docker development

Usually I don’t bother setting up SSL for local development but sometimes you’ll be using a service that requires it. Plus, more and more browsers are pushing you towards SSL all the time. [Side note, I wish someone would make a browser just for developers that gets out of the way and stops trying to protect us from ourselves when we’re working on local sites.]

A lot of folks are moving towards using Let’s Encrypt for free SSL certs but I’m either stubborn or dumb. Basically I didn’t want to figure out how to work it into a Docker container system, so I went old school and created a self-signed certificate. Please note you WILL need to add a “security exception” to your browser when you’re done with this process.

First off, you need openssl. I ~think~ I installed OpenSSL via Chocolatey install openssl on Windows, but honestly it was a while ago. You could also use the Openssl that comes in Ubuntu for Windows.

The command to create a self-signed cert is:

openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -subj "/C=US/ST=NC/L=Local/O=Dev/CN=mysite.local" -keyout ./ssl.key -out ./ssl.crt

First off, credit for this goes to StackExchange user THelper and in all honesty I don’t know exactly what all the parameters mean. (I’m so trusting!)

Here’s what you need to know to get this to work. ssl.key and ssl.crt are the filenames you’ll be generating. This string “/C=US/ST=NC/L=Local/O=Dev/CN=mysite.local” has to be customized:

/C = country code (I’m in the US)
/ST = state (North Carolina for me)
/L = locale… as you can see I fudged this
/O = Organization… again, fudged. All these matter if you’re creating a real cert to use online but for local.. meh
/CN = domain name. This one is important. I’m working on https://mysite.local

So edit that string and run the command and it should generate ssl.key and ssl.crt files.

I use docker-compose so I need to tweak my docker-compose.yml file to expose port 443 (the ssl port):

ports:
- 80:80
- 443:443

And in your dockerfile you need to copy the keys to your container. To do this, first drop them in the same directory as your docker file (I tried keeping them outside the directory but would get “Forbidden path outside the build context” errors).

Next, add this to your docker file:

RUN mkdir -p /etc/apache2/ssl/
COPY ./ssl.crt /etc/apache2/ssl/ssl.crt
COPY ./ssl.key /etc/apache2/ssl/ssl.key

So we’re creating a directory and copying the files into it.

Also remember to expose port 443 in your docker file:

EXPOSE 80
EXPOSE 443

Now you need to update your apache virtual hosts to support ssl.

Here’s part of my Virtual Host file, the first 3 lines are what we’re focused on, notice they match the directory we created in our docker file:

<VirtualHost *:443>
 SSLEngine On
 SSLCertificateFile /etc/apache2/ssl/ssl.crt
 SSLCertificateKeyFile /etc/apache2/ssl/ssl.key

ServerName mysite.local

ServerAdmin webmaster@localhost
 DocumentRoot /var/www/html

 ErrorLog ${APACHE_LOG_DIR}/error.log
 CustomLog ${APACHE_LOG_DIR}/access.log combined

<Directory "/var/www/html">
  Require all granted
  AllowOverride All
  Options Indexes FollowSymLinks
 </Directory>
 </VirtualHost>

And that should do it!