Laradock & Traefik

To avoid the pain of setting up Let's Encrypt SSL and to work with a better load balancer / reverse proxy I decided to do a Laradock & Traefik setup. And I must say I have been pleasantly surprised so far!

Docker Base

As usual I use my Docker Base . I did do a small tweak so we can use Apache htpasswd for basic user authentication for Traefik console. With the Docker Base Ansible package

  • Docker,
  • Docker Compose,
  • a UFW basic firewall setup and
  • sudo user

are added on the fly. So you do not have to worry about a setup with a missing sudo user, Docker or Docker Compose and a basic firewall.

DNS

Do make sure you have the domains you want to use directing to the ip address of your server. In my case I have the DNS for larastud.io managed by Digital Ocean and there under network I added an A and AAA record for both the sub domain and FQDN.

LSDock

Then I git cloned my Laradock repo . It contains all Laradock goodies as well as a new traefik directory and several extra Docker Compose files. The production file we will not use here setting things up with SSL using Certbot, but the one starting with Traefik. It will also need some tweaks

Traefik Configuration

I then tweaked the .toml file (htpasswd password and email address docktored). This is the Traefik configuration file where you add the entry points and Let’s Encrypt ACME stuff:

Traefik .toml file:

debug = false

logLevel = "ERROR"

defaultEntryPoints = ["http", "https"]

[api]
dashboard = true
entrypoint = "webentry"

[entrypoints]
#...
# https://docs.traefik.io/configuration/api/#authentication
# sudo apt-get install apache2-utils
# htpasswd -nb admin secure_password
[entrypoints.webentry]
address = ":8080"
[entrypoints.webentry.auth]
[entrypoints.webentry.auth.basic]
users = ["admin:key"]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]

[acme]
email = "me@gmail.com"
storage = "acme.json"
entryPoint = "https"
onHostRule = true
onDemand = false
[acme.httpChallenge]
entryPoint = "http"
[[acme.domains]]
main = "larastud.io"
sub = "monitor.larastud.io"

As you can see I also added authentication. Password generated with htpasswd . The Apache utils have already been generated using Docker Base so no need to install those again.

.Env

I also made sure Laradock  .env file was added. One with the new Traefik variables

### TRAEFIK #################################################
TRAEFIK_HOST_HTTP_PORT=80
TRAEFIK_HOST_HTTPS_PORT=443

You can use the example one and tweak the passwords and or user names for MariaDB and such. Just add the rules for Traefik as well.

Launch Containers

Then I launched the containers:

docker-compose -f traefik-docker-compose.yml up -d

using this docker-compose file which is also in the lsdock repo:

version: '3'

networks:
frontend:
driver: ${NETWORKS_DRIVER}
backend:
driver: ${NETWORKS_DRIVER}

volumes:
redis:
driver: ${VOLUMES_DRIVER}
mariadb:
driver: ${VOLUMES_DRIVER}

services:

### Workspace Utilities ##################################
workspace:
build:
context: ./workspace
args:
- LARADOCK_PHP_VERSION=${PHP_VERSION}
- LARADOCK_PHALCON_VERSION=${PHALCON_VERSION}
- INSTALL_SUBVERSION=${WORKSPACE_INSTALL_SUBVERSION}
- INSTALL_XDEBUG=${WORKSPACE_INSTALL_XDEBUG}
- INSTALL_PHPDBG=${WORKSPACE_INSTALL_PHPDBG}
- INSTALL_BLACKFIRE=${INSTALL_BLACKFIRE}
- INSTALL_SSH2=${WORKSPACE_INSTALL_SSH2}
- INSTALL_GMP=${WORKSPACE_INSTALL_GMP}
- INSTALL_SOAP=${WORKSPACE_INSTALL_SOAP}
- INSTALL_LDAP=${WORKSPACE_INSTALL_LDAP}
- INSTALL_IMAP=${WORKSPACE_INSTALL_IMAP}
- INSTALL_MONGO=${WORKSPACE_INSTALL_MONGO}
- INSTALL_AMQP=${WORKSPACE_INSTALL_AMQP}
- INSTALL_PHPREDIS=${WORKSPACE_INSTALL_PHPREDIS}
- INSTALL_MSSQL=${WORKSPACE_INSTALL_MSSQL}
- INSTALL_NODE=${WORKSPACE_INSTALL_NODE}
- NPM_REGISTRY=${WORKSPACE_NPM_REGISTRY}
- INSTALL_YARN=${WORKSPACE_INSTALL_YARN}
- INSTALL_NPM_GULP=${WORKSPACE_INSTALL_NPM_GULP}
- INSTALL_NPM_BOWER=${WORKSPACE_INSTALL_NPM_BOWER}
- INSTALL_NPM_VUE_CLI=${WORKSPACE_INSTALL_NPM_VUE_CLI}
- INSTALL_DRUSH=${WORKSPACE_INSTALL_DRUSH}
- INSTALL_DRUPAL_CONSOLE=${WORKSPACE_INSTALL_DRUPAL_CONSOLE}
- INSTALL_AEROSPIKE=${WORKSPACE_INSTALL_AEROSPIKE}
- AEROSPIKE_PHP_REPOSITORY=${AEROSPIKE_PHP_REPOSITORY}
- INSTALL_V8JS=${WORKSPACE_INSTALL_V8JS}
- COMPOSER_GLOBAL_INSTALL=${WORKSPACE_COMPOSER_GLOBAL_INSTALL}
- COMPOSER_REPO_PACKAGIST=${WORKSPACE_COMPOSER_REPO_PACKAGIST}
- INSTALL_WORKSPACE_SSH=${WORKSPACE_INSTALL_WORKSPACE_SSH}
- INSTALL_LARAVEL_ENVOY=${WORKSPACE_INSTALL_LARAVEL_ENVOY}
- INSTALL_LARAVEL_INSTALLER=${WORKSPACE_INSTALL_LARAVEL_INSTALLER}
- INSTALL_DEPLOYER=${WORKSPACE_INSTALL_DEPLOYER}
- INSTALL_PRESTISSIMO=${WORKSPACE_INSTALL_PRESTISSIMO}
- INSTALL_LINUXBREW=${WORKSPACE_INSTALL_LINUXBREW}
- INSTALL_MC=${WORKSPACE_INSTALL_MC}
- INSTALL_SYMFONY=${WORKSPACE_INSTALL_SYMFONY}
- INSTALL_PYTHON=${WORKSPACE_INSTALL_PYTHON}
- INSTALL_IMAGE_OPTIMIZERS=${WORKSPACE_INSTALL_IMAGE_OPTIMIZERS}
- INSTALL_IMAGEMAGICK=${WORKSPACE_INSTALL_IMAGEMAGICK}
- INSTALL_TERRAFORM=${WORKSPACE_INSTALL_TERRAFORM}
- INSTALL_DUSK_DEPS=${WORKSPACE_INSTALL_DUSK_DEPS}
- INSTALL_PG_CLIENT=${WORKSPACE_INSTALL_PG_CLIENT}
- INSTALL_PHALCON=${WORKSPACE_INSTALL_PHALCON}
- INSTALL_SWOOLE=${WORKSPACE_INSTALL_SWOOLE}
- INSTALL_LIBPNG=${WORKSPACE_INSTALL_LIBPNG}
- INSTALL_IONCUBE=${WORKSPACE_INSTALL_IONCUBE}
- INSTALL_MYSQL_CLIENT=${WORKSPACE_INSTALL_MYSQL_CLIENT}
- PUID=${WORKSPACE_PUID}
- PGID=${WORKSPACE_PGID}
- CHROME_DRIVER_VERSION=${WORKSPACE_CHROME_DRIVER_VERSION}
- NODE_VERSION=${WORKSPACE_NODE_VERSION}
- YARN_VERSION=${WORKSPACE_YARN_VERSION}
- DRUSH_VERSION=${WORKSPACE_DRUSH_VERSION}
- TZ=${WORKSPACE_TIMEZONE}
- BLACKFIRE_CLIENT_ID=${BLACKFIRE_CLIENT_ID}
- BLACKFIRE_CLIENT_TOKEN=${BLACKFIRE_CLIENT_TOKEN}
volumes:
- ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}
extra_hosts:
- "dockerhost:${DOCKER_HOST_IP}"
# ports:
# - "${WORKSPACE_SSH_PORT}:22"
tty: true
environment:
- PHP_IDE_CONFIG=${PHP_IDE_CONFIG}
networks:
- frontend
- backend
labels:
- traefik.enable=false

### PHP-FPM ##############################################
php-fpm:
build:
context: ./php-fpm
args:
- LARADOCK_PHP_VERSION=${PHP_VERSION}
- LARADOCK_PHALCON_VERSION=${PHALCON_VERSION}
- INSTALL_XDEBUG=${PHP_FPM_INSTALL_XDEBUG}
- INSTALL_PHPDBG=${PHP_FPM_INSTALL_PHPDBG}
- INSTALL_BLACKFIRE=${INSTALL_BLACKFIRE}
- INSTALL_SSH2=${PHP_FPM_INSTALL_SSH2}
- INSTALL_SOAP=${PHP_FPM_INSTALL_SOAP}
- INSTALL_IMAP=${PHP_FPM_INSTALL_IMAP}
- INSTALL_MONGO=${PHP_FPM_INSTALL_MONGO}
- INSTALL_AMQP=${PHP_FPM_INSTALL_AMQP}
- INSTALL_MSSQL=${PHP_FPM_INSTALL_MSSQL}
- INSTALL_ZIP_ARCHIVE=${PHP_FPM_INSTALL_ZIP_ARCHIVE}
- INSTALL_BCMATH=${PHP_FPM_INSTALL_BCMATH}
- INSTALL_GMP=${PHP_FPM_INSTALL_GMP}
- INSTALL_PHPREDIS=${PHP_FPM_INSTALL_PHPREDIS}
- INSTALL_MEMCACHED=${PHP_FPM_INSTALL_MEMCACHED}
- INSTALL_OPCACHE=${PHP_FPM_INSTALL_OPCACHE}
- INSTALL_EXIF=${PHP_FPM_INSTALL_EXIF}
- INSTALL_AEROSPIKE=${PHP_FPM_INSTALL_AEROSPIKE}
- AEROSPIKE_PHP_REPOSITORY=${AEROSPIKE_PHP_REPOSITORY}
- INSTALL_MYSQLI=${PHP_FPM_INSTALL_MYSQLI}
- INSTALL_PGSQL=${PHP_FPM_INSTALL_PGSQL}
- INSTALL_PG_CLIENT=${PHP_FPM_INSTALL_PG_CLIENT}
- INSTALL_INTL=${PHP_FPM_INSTALL_INTL}
- INSTALL_GHOSTSCRIPT=${PHP_FPM_INSTALL_GHOSTSCRIPT}
- INSTALL_LDAP=${PHP_FPM_INSTALL_LDAP}
- INSTALL_PHALCON=${PHP_FPM_INSTALL_PHALCON}
- INSTALL_SWOOLE=${PHP_FPM_INSTALL_SWOOLE}
- INSTALL_IMAGE_OPTIMIZERS=${PHP_FPM_INSTALL_IMAGE_OPTIMIZERS}
- INSTALL_IMAGEMAGICK=${PHP_FPM_INSTALL_IMAGEMAGICK}
- INSTALL_CALENDAR=${PHP_FPM_INSTALL_CALENDAR}
- INSTALL_FAKETIME=${PHP_FPM_INSTALL_FAKETIME}
- INSTALL_IONCUBE=${PHP_FPM_INSTALL_IONCUBE}
- INSTALL_YAML=${PHP_FPM_INSTALL_YAML}
volumes:
- ./php-fpm/php${PHP_VERSION}.ini:/usr/local/etc/php/php.ini
- ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}
expose:
- "9000"
extra_hosts:
- "dockerhost:${DOCKER_HOST_IP}"
environment:
- PHP_IDE_CONFIG=${PHP_IDE_CONFIG}
- FAKETIME=${PHP_FPM_FAKETIME}
restart: unless-stopped
depends_on:
- workspace
networks:
- backend
labels:
- traefik.enable=false

### PHP Worker ############################################
php-worker:
build:
context: ./php-worker
args:
- PHP_VERSION=${PHP_VERSION}
- INSTALL_PGSQL=${PHP_WORKER_INSTALL_PGSQL}
- INSTALL_BCMATH=${PHP_WORKER_INSTALL_BCMATH}
- INSTALL_SOAP=${PHP_WORKER_INSTALL_SOAP}
- INSTALL_ZIP_ARCHIVE=${PHP_WORKER_INSTALL_ZIP_ARCHIVE}
volumes:
- ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}
- ./php-worker/supervisord.d:/etc/supervisord.d
restart: unless-stopped
depends_on:
- workspace
extra_hosts:
- "dockerhost:${DOCKER_HOST_IP}"
networks:
- backend
labels:
- traefik.enable=false

### NGINX Server #########################################
nginx:
build:
context: ./nginx
args:
- PHP_UPSTREAM_CONTAINER=${NGINX_PHP_UPSTREAM_CONTAINER}
- PHP_UPSTREAM_PORT=${NGINX_PHP_UPSTREAM_PORT}
- CHANGE_SOURCE=${CHANGE_SOURCE}
volumes:
- ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}
- ${NGINX_HOST_LOG_PATH}:/var/log/nginx
- ${NGINX_SITES_PATH}:/etc/nginx/sites-available
- ${NGINX_SSL_PATH}:/etc/nginx/ssl
# ports:
# - "${NGINX_HOST_HTTP_PORT}:80"
# - "${NGINX_HOST_HTTPS_PORT}:443"
restart: unless-stopped
depends_on:
- php-fpm
networks:
- frontend
- backend
labels:
- traefik.backend=nginx
- traefik.frontend.rule=Host:larastud.io
- traefik.docker.network=frontend
- traefik.port=80

### MariaDB ##############################################
mariadb:
build: ./mariadb
volumes:
- ${DATA_PATH_HOST}/mariadb:/var/lib/mysql
- ${MARIADB_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d
# https://github.com/laradock/laradock/issues/571#issuecomment-280874407
# https://github.com/laradock/laradock/pull/339/commits/ec3ebb9701de24d9c64d567c786a1601a5626f2d
# ports:
# - "${MARIADB_PORT}:3306"
restart: unless-stopped
environment:
- MYSQL_DATABASE=${MARIADB_DATABASE}
- MYSQL_USER=${MARIADB_USER}
- MYSQL_PASSWORD=${MARIADB_PASSWORD}
- MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}
networks:
- backend
labels:
- traefik.enable=false

### Redis ################################################
redis:
build: ./redis
volumes:
- ${DATA_PATH_HOST}/redis:/data
# ports:
# - "${REDIS_PORT}:6379"
restart: unless-stopped
networks:
- backend
labels:
- traefik.enable=false

### TRAEFIK #########################################
traefik:
build:
context: ./traefik
command: --docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "${TRAEFIK_HOST_HTTP_PORT}:80"
- "${TRAEFIK_HOST_HTTPS_PORT}:443"
networks:
- frontend
- backend
labels:
- traefik.backend=traefik
- traefik.frontend.rule=Host:monitor.larastud.io
- traefik.port=8080

Immediately monitor.larastud.io and larastud.io started working with Let’s Encrypt SSL certificates. Only the main site showed an error 500. This was because I had not run composer yet nor had I added the Laravel .env file. So added .env and then got into workspace to run composer:

docker-compose exec --user=laradock workspace bash
laradock@3d330cdb93ea:/var/www$ composer install

After that the main site was loading as well. Then I wondered about the nginx.conf… The set up I had with default was no longer being used now was it?. Well the SSl with self signed certificate was no longer being used. The proper root path was still being used. As for PHP, purrin’ like a kitten. Added a quick PHP Info and saw it in all its glory.

Jasper Frumau

Jasper has been working with web frameworks and applications such as Laravel, Magento and his favorite CMS WordPress including Roots Trellis and Sage for more than a decade. He helps customers with web design and online marketing. Services provided are web design, ecommerce, SEO, content marketing. When Jasper is not coding, marketing a website, reading about the web or dreaming the internet of things he plays with his son, travels or run a few blocks.