Why
Docker is a good option for setting up local development environments. Especially on Windows, where Magento2 won't work natively. Alpine is a good base image, since it's very light, ~5MB uncompressed and ~2MB compressed. As opposed to something like Debian that starts at ~50MB compressed.
This setup works both on Windows and Linux, most likely on MacOS as well.
How
Get the Magento2 source code
This can be done either from Magento developer site, or downloading the repo as a zip from git.
Copy the zip
Copy the zip or even better make a symbolic link(it must be a hard link) in the folder with the Dockerfile This is needed because Dockerfile can't reference files outside its directory.
Create the Dockerfile
You can find the sample Dockerfile contents below.
- The php-fpm image doesn't have the modules needed by Magento enabled by default, so those have to be enabled
- Opcache is not enabled by default, and it makes a big difference in performance
- The magento source code is copied into the container itself, this makes things much snappier in Docker on Windows
- The script also installs all the composer packages required by magento
Create a docker-compose.yaml
- Magento requires a DB and a server, docker-compose is a convenient way to launch them all at once in a single network
- Notice that the Magento source is shared between php-fpm and nginx containers via a volume, nginx needs the magento source to match the requests, which it then forwards to the php-fpm via fastcgi
- We reference the Dockerfile by name, since it's more convenient to use between projects, but you can also replace
image: magento:2.3-alpine
withbuild: .
to just build the Dockerfile without creating an image - We map the port
80
from the nginx container to port8080
on localhost, you can use any port you like on the local machine
Create site.conf file
- This this is the nginx config, by default nginx can't properly route Magento source, so, we have to import the nginx config that comes with Magento
- It will be copied into the nginx container to be picked up as the default configuration
Create php.ini file
- This is a simple way to configure the php settings inside the php-fpm container
- It will be copied into the php-fpm container
Run docker build
Run docker build (this might take a while)
- We create a few intermediate containers for convenience, to remove them once the final container is build use the
--rm
parameter
Run docker-compose
Run docker-compose up
- This will launch all the containers in docker-compose, and create a network between them
Complete the setup
Navigate to http://127.0.0.1:8080/setup
or any port you chose, to complete the setup
- Alternatively you can jump into the running php container and run something like
php bin/magento setup ..
- You can do that by running
docker exec -it {folder_name}_php_1 /bin/sh
- Then
cd /var/www/html/magento
- Then you can run any magento command
php bin/magento setup:di:compile
and so on - If you get an out of memory error, you can launch php with more RAM allocated via
php -d memory_limit=1024M ...
or set that in php.ini - Probably not worth setting up
localhost
as the sitename, as Magento seems to have issues with it, use a custom record in hosts file, or use127.0.0.1
Setup the DB details
Use the following db details to configure magento, they are modifiable in the Dockerfile
- Database host: mysql
- Database name:
magento
- User:
user
- Password:
user1
Installing the module
To install the module you'll probably want to jump into the php container via docker exec -it {folder_name}_php_1 /bin/sh
, mentioned above
Query the db from via the CLI
To query the db from the cli, you'll have to start a new container in the same network
docker run --network {folder_name}_main-network -it alpine
- Then install mariadb client
apk add --no-cache mariadb-client
- The run the mariadb client
mysql -h mysql -u user -p
and enter the password from the docker-compose - You can also add docker image with phpmyadmin in it, to the docker-compose
Create CLI aliases
You can create some aliases for simple access to magento command line
Bash
# Set a bash alias
alias magento='docker exec -it {folder_name}_php_1 /var/www/html/magento/bin/magento'
# Helper to get the logs
alias magento-logs='docker exec -it {folder_name}_php_1 tail -f -n 10 /var/www/html/magento/var/log/debug.log'
# Then use it to run commands against magento inside the container
magento module:status
Fish
alias magento 'docker exec -it {folder_name}_php_1 php /var/www/html/magento/bin/magento'
magento module:status
PowerShell
function magento { docker exec -it {folder_name}_php_1 php /var/www/html/magento/bin/magento $args }
magento module:status
Your directory tree should look something like this:
> tree
.
├── docker-compose.yaml
├── Dockerfile
├── magento.zip
├── site.conf
└── Vendor
└── Module
├── etc
│ └── module.xml
└── registration.php
On Linux a hard symbolic link can be created via the ln
command.
Example of creating a symlinc on Linux, building an image and running docker-compose up.
ln ~/Downloads/magento2-2.3.zip ./magento.zip
docker build --rm -t magento:2.3 .
docker-compose up
Attachments
Dockerfile
FROM php:7.3-fpm-alpine AS fpm-magento-base
RUN apk add --no-cache libpng-dev icu-dev libxml2-dev libxslt-dev zip libzip-dev libjpeg-turbo-dev freetype-dev && \
docker-php-ext-configure gd --with-jpeg-dir=/usr/include --with-freetype-dir=/usr/include && \
docker-php-ext-install -j$(nproc) pdo_mysql bcmath gd intl soap xsl zip sockets opcache && \
php -r "copy('https://getcomposer.org/installer', '/root/composer-setup.php');" && \
php /root/composer-setup.php --install-dir=/usr/bin
FROM fpm-magento-base AS fpm-magento-base-with-dependencies
COPY ./magento.zip /var/www/html/magento.zip
RUN unzip /var/www/html/magento.zip && \
rm /var/www/html/magento.zip && \
cd /var/www/html && \
mv magento* magento && \
cd magento && \
php /usr/bin/composer.phar install
FROM fpm-magento-base-with-dependencies
RUN cd /var/www/html/magento && \
php /usr/bin/composer.phar install && \
php bin/magento module:enable --all && \
php -d memory_limit=1024M bin/magento setup:di:compile && \
mkdir var/cache && \
chmod -R 777 var && \
chmod -R 777 app && \
chmod -R 777 pub && \
chmod -R 777 generated && \
php /usr/bin/composer.phar clearcache
docker-compose.yaml
version: "3"
services:
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- app:/var/www/html
- ./site.conf:/etc/nginx/conf.d/default.conf
networks:
- main-network
depends_on:
- php
php:
image: magento:2.3
networks:
- main-network
volumes:
- app:/var/www/html
- ./Vendor:/var/www/html/magento/app/code/Vendor
mysql:
image: jbergstroem/mariadb-alpine
environment:
MYSQL_USER: user
MYSQL_PASSWORD: user1
MYSQL_DATABASE: magento
expose:
- "3306"
volumes:
- db:/var/lib/mysql
- ./php.ini:/usr/local/etc/php/php.ini
networks:
- main-network
networks:
main-network:
driver: bridge
volumes:
db:
driver: local
app:
driver: local
site.conf
upstream fastcgi_backend {
server php:9000;
}
server {
listen 80;
server_name localhost;
set $MAGE_ROOT /var/www/html/magento;
include /var/www/html/magento/nginx.conf.sample;
}
php.ini
max_input_vars 10000
memory_limit 1024M