Warden is a CLI utility for orchestrating Docker-based developer environments and enables multiple local environments to run simultaneously without port conflicts. docker-compose
is used to control everything that Warden runs.
Using Warden, a custom environment can be set up for each project. The environment can also be overridden or extended.
Warden supports the following applications and frameworks on macOS and Linux environments:
- Magento 1
- Magento 2
- Laravel
- Symfony 4
- Shopware 6
Warden provides the following features:
Traefik: for SSL termination and routing/proxying requests into the correct containers.
Portainer: for quick visibility into what’s running inside the local Docker host.
Dnsmasq: to serve DNS responses for .test
domains eliminating manual editing of /etc/hosts
SSH tunnel: for connecting from Sequel Pro or TablePlus into any one of multiple running database containers.
SSL Certificates: Warden issued wildcard SSL certificates for running https on all local development domains.
Prerequisites
Docker and docker-compose should be installed before installing Warden.
The easiest and recommended way to get Docker Compose is to install Docker Desktop.
Docker Desktop includes Docker Compose along with Docker Engine and Docker CLI which are Compose prerequisites.
Install Warden
Warden can be installed via Homebrew on both macOS and Linux hosts:
brew install davidalger/warden/warden
Start Warden Global Services
warden svc up
The started global services are run in different containers. They can be checked as below:
Check the running containers
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42b348ff6e55 traefik:2.2 "/entrypoint.sh trae…" About a minute ago Up 51 seconds 127.0.0.1:80->80/tcp, 127.0.0.1:443->443/tcp traefik
3768311d9211 panubo/sshd:1.1.0 "/entry.sh /usr/sbin…" About a minute ago Up 53 seconds 127.0.0.1:2222->22/tcp tunnel
fa246c804b67 wardenenv/mailhog:1.0 "MailHog" About a minute ago Up 56 seconds 1025/tcp, 8025/tcp mailhog
ab1bd5f4c843 portainer/portainer-ce "/portainer" About a minute ago Up 57 seconds 8000/tcp, 9000/tcp, 9443/tcp portainer
caaf32a6a2b2 jpillora/dnsmasq "/bin/sh -c ' echo \"…" About a minute ago Up 55 seconds 127.0.0.1:53->53/udp dnsmasq
The above global services can be accessed from the following URLs:
https://traefik.warden.test/
https://portainer.warden.test/
https://dnsmasq.warden.test/
https://mailhog.warden.test/
Install Magento 2 using Docker & Warden
Create a new directory for your Magento 2 project and go inside that directory.
I have created a directory named your-project
in the example below:
mkdir -p ~/your-path/your-project
cd ~/your-path/your-project
Generate environment file
The environment file is needed for Warden and docker to work with the project.
From your Magento 2 project’s root folder, run the following commands:
warden env-init your-project magento2
This will generate an environment file .env
.
~/your-path/your-project/.env
WARDEN_ENV_NAME=your-project
WARDEN_ENV_TYPE=magento2
WARDEN_WEB_ROOT=/
TRAEFIK_DOMAIN=your-project.test
TRAEFIK_SUBDOMAIN=app
WARDEN_DB=1
WARDEN_ELASTICSEARCH=1
WARDEN_VARNISH=1
WARDEN_RABBITMQ=1
WARDEN_REDIS=1
ELASTICSEARCH_VERSION=7.6
MARIADB_VERSION=10.3
NODE_VERSION=10
COMPOSER_VERSION=1
PHP_VERSION=7.4
PHP_XDEBUG_3=1
RABBITMQ_VERSION=3.8
REDIS_VERSION=5.0
VARNISH_VERSION=6.0
WARDEN_SYNC_IGNORE=
WARDEN_ALLURE=0
WARDEN_SELENIUM=0
WARDEN_SELENIUM_DEBUG=0
WARDEN_BLACKFIRE=0
WARDEN_SPLIT_SALES=0
WARDEN_SPLIT_CHECKOUT=0
WARDEN_TEST_DB=0
WARDEN_MAGEPACK=0
BLACKFIRE_CLIENT_ID=
BLACKFIRE_CLIENT_TOKEN=
BLACKFIRE_SERVER_ID=
BLACKFIRE_SERVER_TOKEN=
Sign SSL certificate for your domain
The domain should be the same as the value of the TRAEFIK_DOMAIN
variable in the above .env
file.
warden sign-certificate your-project.test
Output:
==> Generating private key your-project.test.key.pem
Generating RSA private key, 2048 bit long modulus
..............+++
......................+++
e is 65537 (0x10001)
==> Generating signing req your-project.test.crt.pem
==> Generating certificate your-project.test.crt.pem
Signature ok
subject=/C=US/O=Warden.dev/CN=your-project.test
Getting CA Private Key
==> Updating traefik
Docker Compose is now in the Docker CLI, try `docker compose up`
traefik is up-to-date
Restarting traefik ... done
Start the project environment
warden env up
This will start the docker containers required for Magento 2 like php-fpm
, nginx
, varnish
, elasticsearch
, maridb
, redis
, and rabbitmq
.
Check the running containers
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6ada603a979e wardenenv/varnish:6.0 "/bin/sh -c 'envsubs…" 12 minutes ago Up 12 minutes 80/tcp your-project_varnish_1
1a078250aa71 wardenenv/nginx:1.16 "/bin/sh -c 'envsubs…" 12 minutes ago Up 12 minutes 80/tcp your-project_nginx_1
efa76857c793 wardenenv/php-fpm:7.4-magento2-xdebug3 "docker-entrypoint p…" 12 minutes ago Up 12 minutes 9000/tcp your-project_php-debug_1
3c7e93d8b618 wardenenv/php-fpm:7.4-magento2 "docker-entrypoint p…" 12 minutes ago Up 12 minutes 9000/tcp your-project_php-fpm_1
64a9ee90817e wardenenv/elasticsearch:7.6 "/usr/local/bin/dock…" 12 minutes ago Up 12 minutes 9200/tcp, 9300/tcp your-project_elasticsearch_1
ef674aa04c92 elastichq/elasticsearch-hq:latest "supervisord -c /etc…" 12 minutes ago Up 12 minutes 5000/tcp your-project_elasticsearch-hq_1
0f239eb38e7d wardenenv/mariadb:10.3 "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 3306/tcp your-project_db_1
6538d62d7740 wardenenv/rabbitmq:3.8 "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 4369/tcp, 5671-5672/tcp, 15671-15672/tcp, 15691-15692/tcp, 25672/tcp your-project_rabbitmq_1
63ec5028b58b wardenenv/redis:5.0 "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 6379/tcp your-project_redis_1
42b348ff6e55 traefik:2.2 "/entrypoint.sh trae…" 24 hours ago Up 18 minutes 127.0.0.1:80->80/tcp, 127.0.0.1:443->443/tcp traefik
3768311d9211 panubo/sshd:1.1.0 "/entry.sh /usr/sbin…" 24 hours ago Up 24 hours 127.0.0.1:2222->22/tcp tunnel
fa246c804b67 wardenenv/mailhog:1.0 "MailHog" 24 hours ago Up 24 hours 1025/tcp, 8025/tcp mailhog
ab1bd5f4c843 portainer/portainer-ce "/portainer" 24 hours ago Up 24 hours 8000/tcp, 9000/tcp, 9443/tcp portainer
caaf32a6a2b2 jpillora/dnsmasq "/bin/sh -c ' echo \"…" 24 hours ago Up 24 hours 127.0.0.1:53->53/udp dnsmasq
Configure global Magento Marketplace credentials
composer global config http-basic.repo.magento.com <username> <password>
The username and password are from the Magento Marketplace Access Keys.
Magento Docs: Get your authentication keys
Username = Public Key
Password = Private Key
The above command will generate ~/.composer/auth.json
file, e.g.
{
"http-basic": {
"repo.magento.com": {
"username": "your-public-key-xxxxxxxx",
"password": "your-private-key-xxxxxxx"
}
}
}
If you already have the
~/.composer/auth.json
file in your host machine, then you don’t need to run the above command to generate the global composer file. Theauth.json
file is mounted into the docker container.
Go inside the php-fpm container shell
warden shell
Output:
www-data@your-project-php-fpm:/var/www/html$
It’s the same as running the following command:
docker exec -it your-project_php-fpm_1 bash
Initialize project source files
META_PACKAGE=magento/project-community-edition META_VERSION=2.4.x
composer create-project --repository-url=https://repo.magento.com/ \
"${META_PACKAGE}" /tmp/your-project "${META_VERSION}"
rsync -a /tmp/your-project/ /var/www/html/
rm -rf /tmp/your-project/
Check project files
www-data@your-project-php-fpm:/var/www/html$ ls -al
total 1804
drwxrwxr-x 12 www-data www-data 4096 Nov 18 05:45 .
drwxr-xr-x 1 root root 4096 Aug 1 04:34 ..
-rw-rw-r-- 1 www-data www-data 343 Nov 18 05:45 .editorconfig
-rw-r--r-- 1 www-data www-data 657 Nov 18 03:55 .env
-rw-rw-r-- 1 www-data www-data 1571 Nov 18 05:45 .gitignore
-rw-rw-r-- 1 www-data www-data 214 Nov 18 05:45 .htaccess
-rw-rw-r-- 1 www-data www-data 11382 Nov 18 05:45 .htaccess.sample
-rw-rw-r-- 1 www-data www-data 1523 Nov 18 05:45 .php-cs-fixer.dist.php
-rw-rw-r-- 1 www-data www-data 101 Nov 18 05:45 .user.ini
-rw-rw-r-- 1 www-data www-data 766543 Nov 18 05:45 CHANGELOG.md
-rw-rw-r-- 1 www-data www-data 650 Nov 18 05:45 COPYING.txt
-rw-rw-r-- 1 www-data www-data 2972 Nov 18 05:45 Gruntfile.js.sample
-rw-rw-r-- 1 www-data www-data 10364 Nov 18 05:45 LICENSE.txt
-rw-rw-r-- 1 www-data www-data 10376 Nov 18 05:45 LICENSE_AFL.txt
-rw-rw-r-- 1 www-data www-data 698 Nov 18 05:45 SECURITY.md
drwxr-xr-x 4 www-data www-data 4096 Nov 18 05:45 app
-rw-rw-r-- 1 www-data www-data 150 Nov 18 05:45 auth.json.sample
drwxr-xr-x 2 www-data www-data 4096 Nov 18 05:45 bin
-rw-r--r-- 1 www-data www-data 2800 Nov 18 05:35 composer.json
-rw-rw-r-- 1 www-data www-data 927187 Nov 18 05:45 composer.lock
drwxr-xr-x 4 www-data www-data 4096 Nov 18 05:45 dev
drwxr-xr-x 2 www-data www-data 4096 Nov 18 05:45 generated
-rw-rw-r-- 1 www-data www-data 55 Nov 18 05:45 grunt-config.json.sample
drwxr-xr-x 4 www-data www-data 4096 Nov 18 05:45 lib
-rw-rw-r-- 1 www-data www-data 6957 Nov 18 05:45 nginx.conf.sample
-rw-rw-r-- 1 www-data www-data 1234 Nov 18 05:45 package.json.sample
drwxr-xr-x 2 www-data www-data 4096 Nov 18 05:45 phpserver
drwxr-xr-x 6 www-data www-data 4096 Nov 18 05:45 pub
drwxr-xr-x 7 www-data www-data 4096 Nov 18 05:45 setup
drwxr-xr-x 2 www-data www-data 4096 Nov 18 05:45 var
drwxrwxr-x 75 www-data www-data 4096 Nov 18 05:45 vendor
Install Magento2 Application
bin/magento setup:install \
--backend-frontname=backend \
--amqp-host=rabbitmq \
--amqp-port=5672 \
--amqp-user=guest \
--amqp-password=guest \
--db-host=db \
--db-name=magento \
--db-user=magento \
--db-password=magento \
--search-engine=elasticsearch7 \
--elasticsearch-host=elasticsearch \
--elasticsearch-port=9200 \
--elasticsearch-index-prefix=magento2 \
--elasticsearch-enable-auth=0 \
--elasticsearch-timeout=15 \
--http-cache-hosts=varnish:80 \
--session-save=redis \
--session-save-redis-host=redis \
--session-save-redis-port=6379 \
--session-save-redis-db=2 \
--session-save-redis-max-concurrency=20 \
--cache-backend=redis \
--cache-backend-redis-server=redis \
--cache-backend-redis-db=0 \
--cache-backend-redis-port=6379 \
--page-cache=redis \
--page-cache-redis-server=redis \
--page-cache-redis-db=1 \
--page-cache-redis-port=6379
Output:
...
...
[SUCCESS]: Magento installation complete.
[SUCCESS]: Magento Admin URI: /backend
Nothing to import.
Configure Magento2 Application
bin/magento config:set --lock-env web/unsecure/base_url \
"https://${TRAEFIK_SUBDOMAIN}.${TRAEFIK_DOMAIN}/"
bin/magento config:set --lock-env web/secure/base_url \
"https://${TRAEFIK_SUBDOMAIN}.${TRAEFIK_DOMAIN}/"
bin/magento config:set --lock-env web/secure/offloader_header X-Forwarded-Proto
bin/magento config:set --lock-env web/secure/use_in_frontend 1
bin/magento config:set --lock-env web/secure/use_in_adminhtml 1
bin/magento config:set --lock-env web/seo/use_rewrites 1
bin/magento config:set --lock-env system/full_page_cache/caching_application 2
bin/magento config:set --lock-env system/full_page_cache/ttl 604800
bin/magento config:set --lock-env catalog/search/enable_eav_indexer 1
bin/magento config:set --lock-env dev/static/sign 0
bin/magento deploy:mode:set -s developer
bin/magento cache:disable block_html full_page
bin/magento indexer:reindex
bin/magento cache:flush
Search-engine & Elasticsearch Configuration for Magento 2.4.x and above
- Only for Magento 2.4.x and above.
- Not required/supported for Magento 2.3.x and below.
bin/magento config:set --lock-env catalog/search/engine elasticsearch7
bin/magento config:set --lock-env catalog/search/elasticsearch7_server_hostname elasticsearch
bin/magento config:set --lock-env catalog/search/elasticsearch7_server_port 9200
bin/magento config:set --lock-env catalog/search/elasticsearch7_index_prefix magento2
bin/magento config:set --lock-env catalog/search/elasticsearch7_enable_auth 0
bin/magento config:set --lock-env catalog/search/elasticsearch7_server_timeout 15
Generate Magento Admin User
The admin username is set as localadmin
below:
ADMIN_PASS="$(pwgen -n1 16)"
ADMIN_USER=localadmin
bin/magento admin:user:create \
--admin-password="${ADMIN_PASS}" \
--admin-user="${ADMIN_USER}" \
--admin-firstname="Local" \
--admin-lastname="Admin" \
--admin-email="${ADMIN_USER}@example.com"
printf "u: %s\np: %s\n" "${ADMIN_USER}" "${ADMIN_PASS}"
Launch the application
Frontend: https://app.your-project.test/
Backend: https://app.your-project.test/backend/
Rabbitmq: https://rabbitmq.your-project.test/
Elasticsearch: https://elasticsearch.your-project.test/
Global Services
https://traefik.warden.test/
https://portainer.warden.test/
https://dnsmasq.warden.test/
https://mailhog.warden.test/
Common Warden Commands
svc Orchestrates global services such as traefik, portainer and dnsmasq via docker-compose
env-init Configure environment by adding '.env' file to the current working directory
env Controls an environment from any point within the root project directory
db Interacts with the db service on an environment (see 'warden db -h' for details)
redis Interacts with the redis service on an environment (see 'warden redis -h' for details)
install Initializes or updates warden configuration on host machine
shell Launches into a shell within the current project environment
debug Launches debug enabled shell within current project environment
sign-certificate Signs a wildcard certificate including all passed hostnames on the SAN list
version Show version information
# Launch shell session inside php-fpm container
warden shell
# Create and start containers
warden env up
# Stop and remove containers
warden env down
# Remove volumes completely
warden env down -v
# Start services/containers
warden env start
# Stop services/containers
warden env stop
# Connect to redis
warden redis
# Flush redis completely
warden redis flushall
# Run redis continous stat mode
warden redis --stat
# Tail the varnish activity log
warden env exec -T varnish varnishlog
# Flush varnish
warden env exec -T varnish varnishadm 'ban req.url ~ .'
# Tail environment nginx and php logs
warden env logs --tail 0 -f nginx php-fpm php-debug
# Monitor database processlist
watch -n 3 "warden db connect -A -e 'show processlist'"
# Import a database (if you don’t have pv installed, use cat instead)
pv /path/to/dump.sql.gz | gunzip -c | warden db import
Hope this helps. Thanks.