How to setup Magento 2 on Docker for development

Likes (1)   Dislikes (0)  

In this tutorial, we will set up Magento 2 development environment on top of Docker. We will install a fresh Magento v2.4.5 on Docker. Assuming we have nothing yet, no code base no database, etc. Everything we will start from scratch on the docker container. We will prepare a docker-compose file for Magento 2 setup and run on localhost.

As we are going to run Magento 2 on docker containers the application is going to work in all different operating systems may it be Windows/Linux/Mac. Because the underlying operating system will be decided by the docker image that we choose. And Linux is the most used OS for PHP development. Windows users may opt for WSL2 with docker desktop.

Basic Requirement for understanding this tutorial content.

Sound docker knowledge and docker-compose knowledge is okay to follow along.

Tools You should have under Your Belt.

The main tools for the basic setup of Magento v2.4.5 include the following.

  • Nginx v1.10 – the most powerful web server I guess
  • PHP v8.1 – the easiest server side programming language
  • PHP-fpm – the PHP server API (PHP Handler)
  • MySQL v8 – the database server
  • Elasticsearch v7 – the catalog search engine

To start with a base image we will go with the webdevops/php-nginx-dev:8.1 docker image for our web service. This docker image contains Nginx v1.10, PHP8.1, PHP-fpm, and all of the PHP extensions required to run the Magento 2 application. You do not have to spend time installing all those software and extensions.

For MySQL and Elasticsearch, we will use official docker images as those are straightforward images no need to do any additional installation.

With that said, we are going to have three services/containers in our docker-compose.yaml file as follows.

  • The web service container
  • The mysql service container
  • The elasticsearch service container

The Web service configuration

YAML
  web:
    image: webdevops/php-nginx-dev:8.1
    container_name: m245web
    environment:
      - WEB_ALIAS_DOMAIN=m245docker.com
      - WEB_DOCUMENT_ROOT=/app
      - PHP_DATE_TIMEZONE=IST
      - PHP_DISPLAY_ERRORS=1
      - PHP_MEMORY_LIMIT=2048M
      - PHP_MAX_EXECUTION_TIME=300
      - PHP_POST_MAX_SIZE=500M
      - PHP_UPLOAD_MAX_FILESIZE=1024M
      - COMPOSER_VERSION=2
    volumes:
      - "./src/:/app"
      - "./nginx/vhost.conf:/opt/docker/etc/nginx/vhost.conf"
    ports:
      - "8200:80"
    links:
      - mysql

Let’s start with the Web service first. Inside the Web service, we will define our domain name, document root, composer version, etc in the environment section. We will map the host path volumes for synchronization to the container. Also, we will define the virtual host configuration for our selected domain name and sync that as well to the container environment.

Additionally, we have to define one host port (the PORT number on the host machine) for the Nginx/HTTP server to be able to access the website from the host machine (the system running DOCKER). And will link the MySQL service (to be defined later) to the Web service so that the MySQL will be ready before starting Web (Service Dependency Configuration in Docker Compose).

The MySQL service configuration

YAML
  mysql:
    image: mysql:8.0-debian
    container_name: m245mysql
    ports:
      - "3200:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=m245
    volumes:
      - "./data:/var/lib/mysql"
      - ".docker/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d"
    command: --innodb-buffer-pool-size=2G --innodb_redo_log_capacity=512M

Next, we will configure our database/MySQL service. For the MySQL service, we will use mysql:8.0-debian the official image. We define the host port to be able to connect to the database from the host machine. We will add the database name and the root user password as environment variables. We also add host path volumes for database files. Also added two MySQL initialization configurations in the command section.

The Elasticsearch service configuration

YAML
  elasticsearch:
    image: elasticsearch:7.17.7
    container_name: m245es01
    environment:
      - xpack.security.enabled=false
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - 9200:9200
    volumes:
      - data01:/usr/share/elasticsearch/data

Again we have selected another official docker image for Elasticsearch. We have defined the host port and host path volume for this service as well along with some environment configurations.

The full Docker Compose File

Let us combine all of our individual services together and declare a docker-compose.yaml file with the following YAML code in it. Please make sure you validate the YML syntax (VS Code will help with this).

YAML
version: "3"
services:
  web:
    image: webdevops/php-nginx-dev:8.1
    container_name: m245web
    environment:
      - WEB_ALIAS_DOMAIN=m245docker.com
      - WEB_DOCUMENT_ROOT=/app
      - PHP_DATE_TIMEZONE=IST
      - PHP_DISPLAY_ERRORS=1
      - PHP_MEMORY_LIMIT=2048M
      - PHP_MAX_EXECUTION_TIME=300
      - PHP_POST_MAX_SIZE=500M
      - PHP_UPLOAD_MAX_FILESIZE=1024M
      - COMPOSER_VERSION=2
    volumes:
      - "./src/:/app"
      - "./nginx/vhost.conf:/opt/docker/etc/nginx/vhost.conf"
    ports:
      - "8200:80"
    links:
      - mysql
  mysql:
    image: mysql:8.0-debian
    container_name: m245mysql
    ports:
      - "3200:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=m245
    volumes:
      - "./data:/var/lib/mysql"
      - ".docker/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d"
    command: --innodb-buffer-pool-size=2G --innodb_redo_log_capacity=512M
  elasticsearch:
    image: elasticsearch:7.17.7
    container_name: m245es01
    environment:
      - xpack.security.enabled=false
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - 9200:9200
    volumes:
      - data01:/usr/share/elasticsearch/data
volumes:
  data01:
    driver: local

The Virtual Host Configuration file for NGINX

YAML
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    server_name m245docker.com;
    index index.php;
    client_max_body_size 1024M;

    set $MAGE_ROOT /app;
    include /app/nginx.conf.sample;
}

Note: make sure to replace all occurrences of “fastcgi_backend” to “php” in the nginx.conf.sample file provided by Magento 2 as webdevops/php-nginx-dev:8.1 ships with that name for the FastCGI server.

Setting up the docker containers and starting them all

Let’s jump into the terminal and do the setup. The folder structure for the setup is like the below one.

ShellScript
 m245
├── data
├── docker-compose.yaml
├── nginx
└── src

The data folder and the .docker will be automatically created. To start with we just need the <project_root_directory>/docker-compose.yaml and the nginx folder to store the virtual host configuration file. The src folder is for storing all Magento 2 files (will be generated during Magento 2 setup).

From the project root directory trigger the following command. Before building the containers for the first time we have to comment on the line include /app/nginx.conf.sample; in vhost.conf file as this file does not exist yet in the path specified. We will uncomment this line once after installing Magento 2 on the container.

ShellScript
docker-compose up

This will pull all required docker images from Docker Hub and prepare all the containers for all our services. And the terminal will be used by the command till we manually stop it. Open another terminal and check for your containers by running docker ps. Then SSH into the Web service container and install Magento 2 by running the following command.

ShellScript
# ssh into the docker container running web service
docker exec -it m245web bash

# inside web service container switch to application user
su application

# go to directory /app
cd /app

# then run magento installation command here

Now we are inside the container. We can switch to the application user by running su application. And then install Magento 2 via Composer (see Magento 2 installation via Composer docs). After getting Composer metadata run composer install to pull the Magento 2 source code inside the container. Then we are ready to run the Magento installation command.

ShellScript
bin/magento setup:install \
--base-url=http://m245docker.com:8200 \
--db-host=mysql \
--db-name=magento245 \
--db-user=root \
--db-password=root \
--admin-firstname=admin \
--admin-lastname=admin \
--admin-email=admin@admin.com \
--admin-user=admin \
--admin-password=admin1234 \
--language=en_US \
--currency=USD \
--timezone=America/Chicago \
--use-rewrites=1 \
--search-engine=elasticsearch7 \
--elasticsearch-host=elasticsearch \
--elasticsearch-port=9200 \
--elasticsearch-index-prefix=magento2 \
--elasticsearch-timeout=15

Now exit out of the container and stop all the containers. Remove the comment from the vhost.config file. Change the fastcgi_backend to php in the nginx.config.sample file and start containers again.

Add one DNS entry in the hosts file for your selected domain name and you can access the Magento 2 site with the domain name and port configuration. Access the database from any of your preferred MySQL client applications and change the web URLs to include the port number. Or change it the Magento 2 way like bellow.

PowerShell
PS D:\magento245\magento245> docker exec -it magento245web /app/bin/magento config:show web/unsecure/base_url
http://magento245docker.com/
PS D:\magento245\magento245> docker exec -it magento245web /app/bin/magento config:set web/unsecure/base_url http://magento245docker.com:8200/
Value was saved.
PS D:\magento245\magento245> docker exec -it magento245web /app/bin/magento config:show web/unsecure/base_url                                 
http://magento245docker.com:8200/
PS D:\magento245\magento245> docker exec -it magento245web /app/bin/magento c:c config                       
Cleaned cache types:
config

Dockerize Your Existing Magento 2 Application

So you already have a running Magento 2 installation and you do not want to create a fresh installation. That is also super easy. You just need to determine what services you are using and create your docker-compose file accordingly. For example you are running your existing Magento 2 installation on top of Apache server or you are using MariaDB or you are using OpenSearch etc. This is all about changing the docker compose services according to your requirement.

Handling the Existing Source Code

The main and the important part is the Magento 2 source code that you already have with you. You just need to copy the source code files into a specific path that is in sync with container in docker-compose config (volumes in web service). Or you can start developing docker artifacts around your existing application source code. Configure your existing application URL in docker-compose file. Port numbers may not matter as long as you are not blocking default port 80/443 or you are not running multiple websites locally on docker at same time.

Importing the Existing Database

The second important part is existing database. How to import this database into our new docker setup. Again that is also so simple. You have to make a backup file (SQL Dump) of your existing database and keep that dump file in “.docker/mysql/docker-entrypoint-initdb.d” directory that we have specified in the volumes section for the mysql service. This will import your database dump file into the docker container database for the first time you run this docker-compose file.

Changing Important Configurations According to Docker Environment

Configure both the docker-compose and Magento 2 as per the new environment. Like change Magento 2 configuration for SQL connection, catalog search configuration, application URL and port number. After configuring all things run the docker-compose file, this will sync you existing application code base into Docker container and you don’t have to create a fresh Magento 2 installation.

Executing Magento 2 Console Commands in Docker Environment

If you are new to Docker, you may be wondering how to run those friendly Magento 2 console commands in this new environment. No worries, you can run those commands right from your project root where the docker-compose file exists. Navigate to the directory in terminal and execute your favorite Magento 2 console command right from your system.

ShellScript
docker exec -it m245web /app/bin/magento setup:di:compile

Here “m245web” is the container name as specified in the docker-compose file. We have to give the absolute path to the bin/magento executable as given above. Like this you can run all your favorite commands. The other way is to SSH into the container.

Conclusion

So here we have discussed a minimal Magento 2 installation on Docker. You can use this approach to create any version of Magento 2 local setup. Just copy and paste the docker-compose file to a new empty project and start making changes as per the required Magento 2 version.

You can also use the Apache server for web service instead of Nginx. Look for the apache docker image. Additional services you want you can try to add into the docker-compose file like Redis, Varnish, OpenSearch, MariaDB, RabbitMQ, etc.

The Web Service docker image that we have used also has support for XDEBUG in case you need it, you can configure it.

See how to configure SSL/TLS/HTTPS for your local development environment.

See how to configure Redis in your docker compose for Magento 2.

Share your experience with us about how you feel about developing Magento 2 with Docker in the comments below. Also, ask questions if you have any doubts about this exercise.

11 thoughts on “How to setup Magento 2 on Docker for development”

  1. Hello Rajeeb,

    Thanks for this informative article.

    I successfully set up Magento with Docker following your article, but I encountered an issue when trying to run setup:upgrade. It is displaying the error message “no alive nodes found in your cluster”.

    do have any idea about this problem?

    Reply
    • This usually happens when you dockerize your existing Magento 2 project. The ElasticSearch configuration already stored on the database does not match with the docker setup. Basically the ElasticSearch host name. In case of non-docker installation all of services are on same server and hence we mention localhost as the ES host. In case of docker installation ES host name is the name of the service mentioned in the docker-compose configuration.

      You can change this configuration from admin page configuration section in catalog area if you can access your backend. Otherwise put below configuration in the env.php file temporarily.

      'system' => [
      'default' => [
      'catalog' => [
      'search' => [
      'elasticsearch7_server_hostname' => 'elasticsearch',
      'elasticsearch7_server_port' => '9200',
      'elasticsearch7_enable_auth' => '0'
      ]
      ]
      ]
      ],

      Reply
    • Thanks for trying my solution. Try to debug with the following points.

      1. Check if css URLs are coming or not in browser console? If coming they are definitely wrong. Try to adjust your Urls in database. Base/static/media url
      2. Check if using port number, then those are applied in all urls or not
      3. Try doing static content deploy forcefully
      Reply
  2. Hi Rajeev,

    We have web application which we are planning to migrate from magento 2.4.3 to magento 2.4.5 codebase and we want to move it to docker container managed by AWS ECS service.
    If we had a dockerfile, we can use jenkins to build a docker image every time a new code is pushed through jenkins and deploy that container

    I want to know how can we deploy code from gitlab to docker containers running through the above blog (with docker compose file) using jenkins.

    Reply
    • Good to know that this article was helpful to you to some extent and you want to extend this to next level. You can achieve your requirements with the help of Jenkins CI/CD pipelines.

      The web image uses volume mapping for the development environment, but for production, we should not use volume mapping instead we should copy all source code files directly into the image. This is generally done with a Dockerfile that will be used for building the docker image and finally, the docker-compose file will use that image which contains all the source code files.

      This makes production more secure as there will be no file change synchronization at any time. In a development environment, this syncing happens always due to the use of volume mapping.

      Use Jenkins pipeline to instruct Jenkins to do the following stages.

      1. Pull code from the GitLab (version control). Provide proper instructions to CI/CD pipeline like the version control repository, its credentials, the branch to pull, etc.
      2. In the Build Trigger section on Jenkins config select appropriate trigger options.
      3. Build the docker image from the Dockerfile
      4. Push the image to AWS ECS

      On the AWS ECS side prepare instructions to re-create containers once get a new version of the docker image file.

      Reply
  3. Hi Rajeev,

    I have gone through this setup and have Docker working fine but keep getting the following error when running docker-compose up.

    nginx: [emerg] “upstream” directive is not allowed here in /app/nginx.conf.sample:9

    I am new to trying to setup Magento & Docker and not sure what I have done wrong, any help would be great.

    Reply
    • Hi Rajeev,

      I have gone through this setup and have Docker working fine but keep getting the following error when running docker-compose up.

      nginx: [emerg] “upstream” directive is not allowed here in /app/nginx.conf.sample:9

      also get this error when removing the upstream in the nginx.conf.sample

      nginx: [emerg] host not found in upstream “php-fpm” in /app/nginx.conf.sample

      I am new to trying to setup Magento & Docker and not sure what I have done wrong, any help would be great.

      Reply
      • Hi AndyW, glad that you are following my tutorial. And your first approach of solving the issue is right from my point of view. But still you are getting some issue and I think I need to review your setup once. If you are okay with this contact me on the contact form. I will be happy to help you.

        Or try looking at the following files in your “web” container. These may help you debug the issue further. Also refer to the docs at https://dockerfile.readthedocs.io/en/latest/content/DockerImages/dockerfiles/php-nginx-dev.html#nginx-layout

        1. cat /opt/docker/etc/nginx/main.conf
        2. ls -l /opt/docker/etc/nginx/conf.d
        3. cat /opt/docker/etc/nginx/conf.d/10-php.conf

        The last file 10-php.conf is the PHP-FPM “upstream” config file and it says “php” as the name of the upstream. Hence in the Magento provided file “nginx.conf.sample” you have to change the “upstream” reference according to your setup.

        Reply

Leave a Comment

Share via
Copy link
Powered by Social Snap