Ever wondered how much time the average Magento 2 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, and often takes longer than it should in a lot of cases.
This article is hopefully just one in a series of articles that will make using Docker less intimidating and help other Magento and Non-Magento developers get up and running quickly. The configurations shared are what we use at www.phpstudios.com but your specific requirements may differ, nonetheless, we 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 post 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 magento-local-env && cd magento-local-env
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.magento.com” >> /etc/hostsexit
4. Create a docker-compose.yaml file and paste the configurations below into it.
vi docker-compose.yamlornano docker-compose.yaml
Docker-compose.yaml’s contents should be as follows:
version: ‘2.1’ services: db: hostname: db.magento2.docker image: ‘mariadb:10.1’ environment: – MYSQL_ROOT_PASSWORD=magento2 – MYSQL_DATABASE=magento2 – MYSQL_USER=magento2 – MYSQL_PASSWORD=magento2 ports: – ‘3306’ volumes: – ‘mymagento-magento-sync:/app:delegated’ – ‘mymagento-magento-db:/var/lib/mysql’ 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:5.0’ volumes: – ‘mymagento-magento-sync:/app:delegated’ ports: – 6379 healthcheck: test: ‘redis-cli ping || exit 1’ interval: 30s timeout: 30s retries: 3 networks: magento: aliases: – redis.magento2.docker elasticsearch: hostname: elasticsearch.magento2.docker image: ‘magento/magento-cloud-docker-elasticsearch:6.5-1.1’ networks: magento: aliases: – elasticsearch.magento2.docker fpm: hostname: fpm.magento2.docker #image: ‘magento/magento-cloud-docker-php:7.3-fpm-1.1’ build: ‘.docker/PHP/’ extends: generic volumes: – ‘mymagento-magento-sync:/app:delegated’ networks: magento: aliases: – fpm.magento2.docker depends_on: db: condition: service_healthy web: hostname: web.magento2.docker image: ‘magento/magento-cloud-docker-nginx:latest-1.1′ extends: generic ports: – ’80:80’ volumes: – ‘mymagento-magento-sync:/app:delegated’ environment: – VIRTUAL_HOST=magento2.docker – VIRTUAL_PORT=80 – HTTPS_METHOD=noredirect – WITH_XDEBUG=1 networks: magento: aliases: – web.magento2.docker depends_on: fpm: condition: service_started varnish: hostname: varnish.magento2.docker image: ‘magento/magento-cloud-docker-varnish:6.2’ networks: magento: aliases: – magento2.docker depends_on: web: condition: service_healthy 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:7.3-cli-1.1’ extends: generic volumes: – ‘mymagento-magento-sync:/app:delegated’ networks: magento-build: aliases: – build.magento2.docker depends_on: db: condition: service_healthy redis: condition: service_healthy elasticsearch: condition: service_healthy deploy: hostname: deploy.magento2.docker image: ‘magento/magento-cloud-docker-php:7.3-cli-1.1’ extends: generic volumes: – ‘mymagento-magento-sync:/app:delegated’ networks: magento: aliases: – deploy.magento2.docker depends_on: db: condition: service_healthy redis: condition: service_healthy elasticsearch: condition: service_healthy fpm_xdebug: hostname: fpm_xdebug.magento2.docker image: ‘magento/magento-cloud-docker-php:7.3-fpm-1.1’ extends: generic ports: – ‘9001:9001’ volumes: – ‘mymagento-magento-sync:/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 mailhog: hostname: mailhog.magento2.docker image: ‘mailhog/mailhog:latest’ restart: always ports: – ‘1025:1025’ – ‘8025:8025’ networks: magento: aliases: – mailhog.magento2.docker volumes: mymagento-magento-sync: driver_opts: type: none device: ‘${PWD}/mymagento-magento-sync’ o: bind mymagento-magento-db: { } 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:7.3-fpm RUN apt-get update && apt-get install -y \ cron \ git \ libfreetype6-dev \ libicu-dev \ libjpeg62-turbo-dev \ libmagickwand-dev \ libmcrypt-dev \ libpng-dev \ libxslt1-dev \ default-mysql-client \ vim \ zip \ libzip-dev \ wget \ net-tools \ netcat RUN docker-php-ext-configure \ gd –with-freetype-dir=/usr/include/ –with-jpeg-dir=/usr/include/ RUN docker-php-ext-install \ bcmath \ gd \ gettext \ intl \ mbstring \ opcache \ pdo_mysql \ soap \ xsl \ zip \ sockets RUN pecl channel-update pecl.php.net \ && pecl install xdebug \ && docker-php-ext-enable xdebug \ && sed -i -e ‘s/^zend_extension/\;zend_extension/g’ /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini RUN pecl install imagick-3.4.3 \ && docker-php-ext-enable imagick 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 EXPOSE 9001
Paste it into .docker/PHP/Dockerfile
vi .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 magento-local-env/.docker/PHP/conf/php.ini
7. Paste the following content into magento-local-env/.docker/PHP/conf/php.ini
memory_limit = 2G max_execution_time = 1800<br>zlib.output_compression = On cgi.fix_pathinfo = 0 date.timezone = UTC
8. Move back into the root dir
cd ../../../
9. Compose the local environment
touch .docker/config.php touch .docker/config.env mkdir mymagento-magento-syncdocker-compose -f docker-compose.yaml up –build -ddocker 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 magento-local-env_db_1 -f
11. Access the fpm containers cli
docker exec -it magento-local-env_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.3.5p2mv project-community-edition/* .rm -rf project-community-edition/
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 {} +<br>find var generated vendor pub/static pub/media app/etc -type d -exec chmod g+ws {} +<br>chown -R :www-data . # Ubuntu<br>chmod u+x bin/magento
14. Create an env.php file
exit && cd mymagento-magento-sync/app/etcsudo && 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 [ ‘MAGE_MODE’ => ‘developer’, ‘cache_types’ => [ ‘compiled_config’ => 1, ‘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, ‘target_rule’ => 1, ‘full_page’ => 1, ‘translate’ => 1, ‘config_webservice’ => 1, ‘vertex’ => 1 ], ‘backend’ => [ ‘frontName’ => ‘admin’ ], ‘db’ => [ ‘connection’ => [ ‘default’ => [ ‘username’ => ‘magento2’, ‘host’ => ‘magento-local-env_db_1’, ‘dbname’ => ‘magento2’, ‘password’ => ‘magento2’ ], ‘indexer’ => [ ‘username’ => ‘magento2’, ‘host’ => ‘magento-local-env_db_1’, ‘dbname’ => ‘magento2’, ‘password’ => ‘magento2’ ] ] ], ‘crypt’ => [ ‘key’ => ” ], ‘resource’ => [ ‘default_setup’ => [ ‘connection’ => ‘default’ ] ], ‘x-frame-options’ => ‘SAMEORIGIN’, ‘session’ => [ ‘save’ => ‘redis’, ‘redis’ => [ ‘host’ => ‘magento-local-env_redis_1’, ‘port’ => 6379, ‘database’ => 0, ‘disable_locking’ => 1 ] ], ‘install’ => [ ‘date’ => ‘Fri, 15 Jun 2018 10:17:40 +0000’ ], ‘static_content_on_demand_in_production’ => 0, ‘force_html_minification’ => 1, ‘cron_consumers_runner’ => [ ‘cron_run’ => false, ‘max_messages’ => 10000, ‘consumers’ => [ ] ], ‘cache’ => [ ‘frontend’ => [ ‘default’ => [ ‘backend’ => ‘Cm_Cache_Backend_Redis’, ‘backend_options’ => [ ‘server’ => ‘magento-local-env_redis_1’, ‘port’ => 6379, ‘database’ => 1 ] ], ‘page_cache’ => [ ‘backend’ => ‘Cm_Cache_Backend_Redis’, ‘backend_options’ => [ ‘server’ => ‘magento-local-env_redis_1’, ‘port’ => 6379, ‘database’ => 2 ] ] ] ], ‘directories’ => [ ‘document_root_is_pub’ => true ], ‘cron’ => [ ], ‘lock’ => [ ‘provider’ => ‘db’, ‘config’ => [ ‘prefix’ => null ] ] ];
15. Go back to the root directory and access the fpm containers cli again
cd ../../../docker exec -it magento-local-env_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=magento-local-env_db_1 \–db-name=magento2 \–db-user=magento2 \–db-password=magento2 \–admin-firstname=admin \–admin-lastname=admin \–admin-email=admin@admin.com \–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. Was it helpful at all in improving your Magento development process? We sure hope so, since we love contributing to the the Magento development community and to the eCommerce development community as a whole.[/vc_column_text][/vc_column][/vc_row]