Introduction:
Ever wondered how much time the average developer spends just setting up a reliable local development environment? The answer definitely varies from organization to organization, the stack in question, and the level of documentation available. I think most developers, no matter what industry or stack they work in, can relate to personally struggling to set up an environment or seeing a developer on their team struggle with the process, however.
When I first started my professional software development journey, I was hired as a Magento 2 intern and this is something I struggled with for a while. At that time, using docker to accomplish my local development goals seemed like a huge undertaking. This article is hopefully just one in a series of articles that will make using Docker less intimidating and help other developers get up and running quickly. The configurations shared are what we use at www.www.peoplelikesoftware.com but your specific requirements may differ, nonetheless, I think this article can help you get to where you want to be in less time. The ins and outs of Docker will be discussed in later articles since including all of that in this one article would probably be information overload.
Instructions:
1. Install docker
sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io
To install a specific version of docker or if you run into any issues, review the documentation at this link https://docs.docker.com/engine/install/ubuntu/
2. Create a new working directory
mkdir docker-setup && cd docker-setup
3. Add a new domain to your /etc/hosts file, this domain will be used to navigate to your local Magento 2 instance
sudo su
echo "127.0.0.1 local.magento245.com" >> /etc/hosts
exit
4. Create a docker-compose.yaml file and paste the configurations below into it.
vi docker-compose.yaml
or
nano docker-compose.yaml
Docker-compose.yaml’s contents should be as follows:
version: '2.1'
services:
db:
hostname: db.magento2.docker
image: 'mysql:8'
environment:
- MYSQL_ROOT_PASSWORD=magento2
- MYSQL_DATABASE=magento2
- MYSQL_USER=magento2
- MYSQL_PASSWORD=magento2
ports:
- '3306:3306'
volumes:
- './demo:/app:delegated'
- './.docker/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d'
healthcheck:
test: 'mysqladmin ping -h localhost'
interval: 30s
timeout: 30s
retries: 3
networks:
magento:
aliases:
- db.magento2.docker
redis:
hostname: redis.magento2.docker
image: 'redis:6.2'
volumes:
- './demo:/app:delegated'
ports:
- 6379
healthcheck:
test: 'redis-cli ping || exit 1'
interval: 30s
timeout: 30s
retries: 3
networks:
magento:
aliases:
- redis.magento2.docker
fpm:
hostname: fpm.magento2.docker
build: '.docker/PHP/'
extends: generic
volumes:
- './demo:/app:delegated'
networks:
magento:
aliases:
- fpm.magento2.docker
depends_on:
db:
condition: service_healthy
fpm_xdebug:
hostname: fpm_xdebug.magento2.docker
image: 'magento/magento-cloud-docker-php:8.1-fpm-1.3.2'
extends: generic
ports:
- '9001:9001'
volumes:
- './demo:/app'
environment:
- 'PHP_EXTENSIONS=bcmath bz2 calendar exif gd gettext intl mysqli pcntl pdo_mysql soap sockets sysvmsg sysvsem sysvshm opcache zip redis xsl sodium xdebug'
- 'XDEBUG_CONFIG=remote_host=host.docker.internal remote_autostart=On remote_enable=On idekey=XDEBUG remote_log=/tmp/xdebug.log remote_port=9000'
networks:
magento:
aliases:
- fpm_xdebug.magento2.docker
depends_on:
db:
condition: service_started
web:
hostname: web.magento2.docker
image: 'magento/magento-cloud-docker-nginx:1.19-1.3.2'
extends: generic
ports:
- '80:80'
volumes:
- './demo:/app:delegated'
networks:
magento:
aliases:
- web.magento2.docker
depends_on:
fpm:
condition: service_started
environment:
- VIRTUAL_HOST=magento2.docker
- VIRTUAL_PORT=80
- HTTPS_METHOD=noredirect
- WITH_XDEBUG=1
varnish:
hostname: varnish.magento2.docker
image: 'magento/magento-cloud-docker-varnish:latest-1.2'
networks:
magento:
aliases:
- magento2.docker
volumes:
- './demo/pub:/etc/varnish'
depends_on:
- 'web'
tls:
hostname: tls.magento2.docker
image: 'magento/magento-cloud-docker-tls:latest-1.1'
ports:
- '443:443'
environment:
HTTPS_UPSTREAM_SERVER_ADDRESS: varnish
networks:
magento:
aliases:
- tls.magento2.docker
depends_on:
varnish:
condition: service_started
generic:
hostname: generic.magento2.docker
image: 'alpine:latest'
env_file: ./.docker/config.env
environment:
- MAGENTO_RUN_MODE=developer
- 'PHP_EXTENSIONS=bcmath bz2 calendar exif gd gettext intl mysqli pcntl pdo_mysql soap sockets sysvmsg sysvsem sysvshm opcache zip sodium redis xsl blackfire'
build:
hostname: build.magento2.docker
image: 'magento/magento-cloud-docker-php:8.1-cli-1.3.2'
extends: generic
volumes:
- './demo:/app:delegated'
networks:
magento-build:
aliases:
- build.magento2.docker
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
deploy:
hostname: deploy.magento2.docker
image: 'magento/magento-cloud-docker-php:8.1-cli-1.3.2'
extends: generic
volumes:
- './demo:/app:delegated'
networks:
magento:
aliases:
- deploy.magento2.docker
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
mailhog:
hostname: mailhog.magento2.docker
image: 'mailhog/mailhog:latest'
restart: on-failure
ports:
- '1025:1025'
- '8025:8025'
networks:
magento:
aliases:
- mailhog.magento2.docker
elasticsearch:
hostname: elasticsearch.magento2.docker
image: 'magento/magento-cloud-docker-elasticsearch:7.11-1.3.2'
networks:
magento:
aliases:
- elasticsearch.magento2.docker
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
environment:
- cluster.name=es-docker
- node.name=node1
mem_limit: 4G
volumes:
demo:
driver_opts:
type: none
device: '${PWD}/demo'
o: bind
mymagento-magento-db: { }
mymagento-maento: { }
networks:
magento:
driver: bridge
magento-build:
driver: bridge
5. Create Dockerfile at the location .docker/PHP/Dockerfile using the commands below
mkdir -p .docker/PHP/ && touch .docker/PHP/Dockerfile
5b. Copy the below content.
FROM php:8.1-fpm
RUN apt-get update && apt-get install -y \
cron \
default-mysql-client \
git \
gnupg \
gzip \
wget \
libbz2-dev \
libfreetype6-dev \
libicu-dev \
libjpeg62-turbo-dev \
libmagickwand-dev \
libmcrypt-dev \
libonig-dev \
libpng-dev \
libsodium-dev \
libssh2-1-dev \
libwebp-dev \
libxslt1-dev \
libzip-dev \
lsof \
mailutils \
msmtp \
procps \
vim \
zip \
default-jdk \
default-jre \
net-tools \
vim \
&& rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-configure \
gd --with-freetype --with-jpeg --with-webp \
&& docker-php-ext-install \
bcmath \
calendar \
exif \
gd \
gettext \
intl \
mbstring \
mysqli \
opcache \
pcntl \
pdo_mysql \
soap \
sockets \
sodium \
sysvmsg \
sysvsem \
sysvshm \
xsl \
zip
RUN pecl channel-update pecl.php.net && pecl install \
imagick \
redis \
ssh2-1.3.1 \
xdebug \
&& pecl clear-cache \
&& rm -rf /tmp/pear \
&& docker-php-ext-enable \
imagick \
redis \
ssh2 \
xdebug \
&& sed -i -e 's/^zend_extension/\;zend_extension/g' /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
RUN curl -sS https://getcomposer.org/installer | \
php -- --install-dir=/usr/local/bin --filename=composer
RUN wget https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 \
&& chmod +x mhsendmail_linux_amd64 \
&& mv mhsendmail_linux_amd64 /usr/local/bin/mhsendmail
COPY conf/php.ini /usr/local/etc/php/
WORKDIR /app
Paste it into .docker/PHP/Dockerfilevi .docker/PHP/Dockerfileornano .docker/PHP/Dockerfile
6. Create a new directory named conf; add a file named php.ini in that directory.
mkdir -p .docker/PHP/conf/cd .docker/PHP/conf/vi php.ini
The path should be docker-setup/.docker/PHP/conf/php.ini
7. Paste the following content into docker-setup/.docker/PHP/conf/php.ini
memory_limit = 4G
max_execution_time = 1800
zlib.output_compression = On
cgi.fix_pathinfo = 0
date.timezone = UTC
xdebug.idekey = PHPSTORM
xdebug.start_with_request = yes
xdebug.discover_client_host = no
xdebug.mode=debug
xdebug.client_host = host.docker.internal
xdebug.client_port = 9000
xdebug.log='/tmp/xdebug.log'
sendmail_path = '/usr/local/bin/mhsendmail --smtp-addr="demo_mailhog_1:1025"'
8. Move back into the root dir
cd ../../../
9. Compose the local environment
touch .docker/config.php
touch .docker/config.env
touch .env
echo "COMPOSE_PROJECT_NAME=demo" >> .env
mkdir demo
docker-compose -f docker-compose.yaml up --build -d
docker ps -a
It’s ok if the build or deploy container has an error or is “unhealthy”.
10. Tail the db container’s logs until you see “ready for connections”
docker logs demo_db_1 -f
11. Access the fpm containers cli
docker exec -it demo_fpm_1 bash
12. Use composer to create and install Magento 2, move all of the contents of the Magento package into the /app dir
You will need to enter your Magento account credentials from https://marketplace.magento.com/customer/accessKeys/
The public key is the username, the private key is the password. If you don’t already have a Magento account/key pair just create one/them. It’s free 🙂
Reference: https://devdocs.magento.com/guides/v2.4/install-gde/prereq/connect-auth.html
composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition=2.4.5
mv project-community-edition/* .
rm -rf project-community-edition/
The public key is the username, the private key is the password. If you don’t already have a Magento account/key pair just create one/them. It’s free 🙂 Enter your Magento account credentials from https://marketplace.magento.com/customer/accessKeys/
13. Set file permissions
find var generated vendor pub/static pub/media app/etc -type f -exec chmod g+w {} +
find var generated vendor pub/static pub/media app/etc -type d -exec chmod g+ws {} +
chown -R :www-data . # Ubuntu
chmod u+x bin/magento
14. Create an env.php file
exit
cd demo/app/etc
sudo vi env.php
Write your own custom configurations or paste the content below into the file.
Fyi, the reason I did not opt to create the file inside the fpm container is that vi doesn’t work as expected when trying to paste something inside the container for some reason.
Sample env.php below :
<?php
return [
'backend' => [
'frontName' => 'admin'
],
'crypt' => [
'key' => ''
],
'db' => [
'table_prefix' => '',
'connection' => [
'default' => [
'host' => 'demo_db_1',
'dbname' => 'magento2',
'username' => 'magento2',
'password' => 'magento2',
'model' => 'mysql4',
'engine' => 'innodb',
'initStatements' => 'SET NAMES utf8;',
'active' => '1'
]
]
],
'resource' => [
'default_setup' => [
'connection' => 'default'
]
],
'x-frame-options' => 'ALLOW-FROM https://app.hubspot.com',
'MAGE_MODE' => 'developer',
'session' => [
'save' => 'files'
],
'cache_types' => [
'config' => 1,
'layout' => 1,
'block_html' => 1,
'collections' => 1,
'reflection' => 1,
'db_ddl' => 1,
'eav' => 1,
'customer_notification' => 1,
'config_integration' => 1,
'config_integration_api' => 1,
'full_page' => 1,
'translate' => 1,
'config_webservice' => 1,
'compiled_config' => 1
]'system' => [
'default' => [
'catalog' => [
'search' => [
'engine' => 'elasticsearch7',
'elasticsearch7_server_hostname' => 'demo_elasticsearch_1',
'elasticsearch7_server_port' => '9200',
'elasticsearch7_index_prefix' => 'magento2_stg'
]
]
]
],
'downloadable_domains' => [
'local.magento245.com',
]
];
15. Go back to the root directory and access the fpm containers cli again
cd ../../../
docker exec -it demo_fpm_1 bash
16. Run the Magento 2 install command inside the container
Reference: https://devdocs.magento.com/guides/v2.4/install-gde/composer.html
bin/magento setup:install \
--base-url=http://local.magento.com \
--db-host=demo_db_1 \--db-name=magento2 \
--db-user=magento2 \
--db-password=magento2 \
--admin-firstname=admin \
--admin-lastname=admin \
[email protected] \
--admin-user=admin \
--admin-password=admin123 \
--language=en_US \
--currency=USD \
--timezone=America/Chicago \
--use-rewrites=1
** If you changed the base-url in the command above, remember to update your /etc/hosts file ***
17. (Optional) Install sample data
bin/magento sampledata:deploy
18. Navigate to local.magento.com or your custom domain and you’re done !!!
If you got this far. Please leave a comment or share. I’d love to hear what you think about the article whether you hated or loved it. This is my first article on Medium so it probably needs some work. Stay tuned for my next article in which I demonstrate how to create a docker-compose file with one command using Magento Cloud’s ece-tools package. Thanks