Magento 2: Development Environment with Docker and Varnish

In this tutorial, I will create a local environment for Magento 2 with Docker and Varnish enabled.

Posted on September 12, 2019 in Magento2

Create a docker folder: m2.docker


mdkir m2.docker
cd m2.docker

Defining some variables for Docker in .env


#inside .env - please use your own password for [your password] placeholder

 CONTAINER_PREFIX=m2docker
 SERVER_NAME=m2.docker
 SERVER_ALIAS=www.m2.docker
 DIRECTORY_NAME=m2.docker
 WEB_USER=techjeffyu
 WEB_ROOT=/var/www/html
 MYSQL_DB_HOST=${CONTAINER_PREFIX}_db_1
 MYSQL_DATABASE=mage2
 MYSQL_ROOT_USERNAME=root
 MYSQL_ROOT_PASSWORD=[your password]
 MYSQL_USER=jyu
 MYSQL_PASSWORD=[your password]
 DOCKER_EXEC=docker exec
 DOCKER_EXEC_INTERACTIVE=docker exec -i
 DOCKER_EXEC_TTY=${DOCKER_EXEC_INTERACTIVE} -t
 VARNISH_PORT=80

Create Docker configuration file:docker-compose.yml

For this docker, we will use Varnish, Apache, PHP7.1, MySQL5.7 and Redis server.


#docker-compose.yml

 # https://docs.docker.com/compose/compose-file
 version: "3.6"
 
 # https://docs.docker.com/compose/compose-file/#service-configuration-reference
 services:
     varnish:
         image: meanbee/magento2-varnish:latest
         environment:
         - VIRTUAL_HOST=m2.docker
         - VIRTUAL_PORT=80
         - HTTPS_METHOD=noredirect
         ports:
         - "80:80"
         links:
         - web
 
     #custom name
     web:
         # https://docs.docker.com/compose/compose-file/#image
         # https://githheizenberg ub.com/udovicic/echo => https://hub.docker.com/r/udovicic/echo/
         image: udovicic/echo:apache-php7.1
 
         # https://docs.docker.com/compose/compose-file/#ports
         ports:
         - "8080:80"
 
         # https://docs.docker.com/compose/compose-file/#expose
         expose:
         - "8080"
 
         # https://docs.docker.com/compose/compose-file/#volumes
         volumes:
         - ./docker/xdebug.ini:/etc/php/7.1/mods-available/xdebug.ini
         - ./docker/php.ini:/etc/php/7.1/fpm/php.ini
         - ./html:/var/www/html
 
         # https://docs.docker.com/compose/compose-file/#environment
         environment:
         - TERM=xterm-256color
         - APACHE_RUN_USER=1000
 
         # https://docs.docker.com/compose/compose-file/#network-configuration-reference
         networks:
             default:
                 aliases:
                 - ${SERVER_NAME}
                 - ${SERVER_ALIAS}
 
     db:
         # https://hub.docker.com/_/mysql/
         image: mysql:5.7
 
         volumes:
         - ./docker/db/data:/var/lib/mysql
 
         environment:
             MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
             MYSQL_DATABASE: ${MYSQL_DATABASE}
             MYSQL_USER: ${MYSQL_USER}
             MYSQL_PASSWORD: ${MYSQL_PASSWORD}
 
     redis:
 
         # https://hub.docker.com/_/redis/
         image: redis:latest

Dockerfile and vcl for Varnish

Dockerfile:


 FROM million12/varnish
 
 MAINTAINER Ash Smith <ash.smith@meanbee.com>
 
 ENV VCL_CONFIG /data/varnish_1.vcl
 ENV VARNISHD_PARAMS -p default_ttl=86400 -p default_grace=86400 -p feature=+esi_ignore_https -p feature=+esi_disable_xml_check
 
 ADD etc/varnish.vcl /data/varnish_1.vcl

default.vcl


 vcl 4.0;
 
 backend default {
     .host = "m2.docker",
     .port = "80"
 }

Create etc folder and create varnish.vcl file inside:


#varnish.vcl
vcl 4.0;

import std;
# The minimal Varnish version is 4.0
# For SSL offloading, pass the following header in your proxy server or load balancer: 'SSL-OFFLOADED: https'

backend default {
    .host = "127.0.0.1";
    .port = "80";
}

sub vcl_recv {

    # Ensure the true IP is sent onwards.
    # This was an issue getting xdebug working through varnish
    # whilst having a proxy infront of varnish (for local docker dev)
    if (req.http.X-Real-Ip) {
        set req.http.X-Forwarded-For = req.http.X-Real-Ip;
    }

    if (req.method == "PURGE") {
        if (!req.http.X-Magento-Tags-Pattern) {
            return (synth(400, "X-Magento-Tags-Pattern header required"));
        }
        ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
        return (synth(200, "Purged"));
    }

    if (req.method != "GET" &&
        req.method != "HEAD" &&
        req.method != "PUT" &&
        req.method != "POST" &&
        req.method != "TRACE" &&
        req.method != "OPTIONS" &&
        req.method != "DELETE") {
          /* Non-RFC2616 or CONNECT which is weird. */
          return (pipe);
    }

    # We only deal with GET and HEAD by default
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # Bypass shopping cart, checkout and search requests
    if (req.url ~ "/checkout" || req.url ~ "/catalogsearch") {
        return (pass);
    }

    # normalize url in case of leading HTTP scheme and domain
    set req.url = regsub(req.url, "^http[s]?://", "");

    # collect all cookies
    std.collect(req.http.Cookie);

    # Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unkown algorithm
            unset req.http.Accept-Encoding;
        }
    }

    # Remove Google gclid parameters to minimize the cache objects
    set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA"
    set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
    set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"

    # static files are always cacheable. remove SSL flag and cookie
        if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") {
        unset req.http.Https;
        unset req.http.SSL-OFFLOADED;
        unset req.http.Cookie;
    }

    return (hash);
}

sub vcl_hash {
    if (req.http.cookie ~ "X-Magento-Vary=") {
        hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));
    }

    # For multi site configurations to not cache each other's content
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # To make sure http users don't see ssl warning
    if (req.http.SSL-OFFLOADED) {
        hash_data(req.http.SSL-OFFLOADED);
    }

}

sub vcl_backend_response {
    if (beresp.http.content-type ~ "text") {
        set beresp.do_esi = true;
    }

    if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") {
        set beresp.do_gzip = true;
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 404) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.uncacheable = true;
        set beresp.ttl = 86400s;
        return (deliver);
    }

    if (beresp.http.X-Magento-Debug) {
        set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
    }

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
        unset beresp.http.set-cookie;
        if (bereq.url !~ "\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") {
            set beresp.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;
        }
    }

   # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass
   if (beresp.ttl <= 0s ||
        beresp.http.Surrogate-control ~ "no-store" ||
        (!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) {
        # Mark as Hit-For-Pass for the next 2 minutes
        set beresp.ttl = 120s;
        set beresp.uncacheable = true;
    }
    return (deliver);
}

sub vcl_deliver {
    if (resp.http.X-Magento-Debug) {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Magento-Cache-Debug = "HIT";
        } else {
            set resp.http.X-Magento-Cache-Debug = "MISS";
        }
    } else {
        unset resp.http.Age;
    }

    unset resp.http.X-Magento-Debug;
    unset resp.http.X-Magento-Tags;
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    unset resp.http.X-Varnish;
    unset resp.http.Via;
    unset resp.http.Link;
}


Create docker folder


cd ..
mkdir docker
cd docker
touch php.ini
touch xdebug.ini

php.ini:


 max_execution_time = 18000
 max_input_time = 1800
 memory_limit = 2048M
 
 session.save_path = "/tmp"
 session.save_handler = files

xdebug.ini:


 zend_extension=xdebug.so
 xdebug.remote_autostart=0
 xdebug.remote_enable=1
 xdebug.remote_port=9000
 xdebug.remote_connect_back=1

Create website folder


cd ..
mkdir html

The final folder tree structure looks like

custom linked product

Install Docker Desktop

Mac: Docker Desktop for Mac

Create Docker and Varnish


#please run following commands:

##start docker
docker-compose up -d

##View docker containers
docker ps

##Stop docker
docker-compose stop

##access one of containers
docker exec -it [container name] /bin/bash

Create a DNS record in /etc/hosts


#/etc/hosts

 127.0.0.1   localhost
 127.0.0.1   m2.docker www.m2.docker

Install Magento 2

Just download Magento 2 source code from https://magento.com, and uncompressed the file into html folder. Then launch your favorite browser and type in http://m2.docker. After that just following the command on the screen. You will create a brand new Magento 2 ecommerce website for development or testing.

Have fun for using/testing Magento 2


comments powered by Disqus