docker-lampose is a template for a LAMP stack development environment running in docker and utilizing docker-compose
.
The main purpose of this template is to automate the startup of PHP projects as far as possible, so you can quickly work on them features of your application.
Disclaimer: This project is not stable! You rather might want to use ddev-local instead. Although some bash scripts of this repo might be helpful.
Also: docker-lampose is currently only tested on Windows.
- local hosting under custom domain (e.g.
https://project.local
) for your PHP project - database import of multiple databases on initial startup into
db
container - easy debugging of browser applications, PHP scripts and remote debug connections with preconfigured
xdebug
composer install
andnpm install
on startup in configured directories- catches outgoing mails and redirects them to a local mail server, so you want accidentally send mails to anyone while testing.
- creation of a locally trusted certificate for
https
connection - use environment variables from the
.env
file in git ignored files for even more automation - automatic pulling of existing git submodules
- automatic pulling of remote git into specific sub directory without the
.git
folder - script to get into the docker container more easily
- a good documentation
Pull this repository and place all of its files in the root directory of your project.
Then make sure to add the following to your .gitignore
file in your root directory:
.env
docker-compose.override.yml
To specify the PHP version change the FROM
command in ./_docker/web/Dockerfile
e.g. for PHP version 5.6:
ARG PHP_VERSION=5.6
FROM php:${PHP_VERSION}-apache
After that make sure to build this container again with docker-compose build
.
To install and enable PHP extensions add them to ./_docker/web/Dockerfile
.
RUN install-php-extensions <extensionname>
If this did not work try this:
RUN docker-php-ext-install <extensionname>
All available extensions see here: https://github.com/mlocati/docker-php-extension-installer#supported-php-extensions
More information on https://hub.docker.com/_/php/ at How to install more PHP extensions
To edit any php.ini
config, just add another .ini
file to _docker/web/additional-inis/
If you need to set the root directory of your web application other than ./
(for example /webroot
) set it in _docker/web/sites-available/000-default.conf
:
# ...
DocumentRoot /var/www/html/webroot
If you need to configure some database parameters (for example innodb_file_format
), you can do that in the _docker/db/my.cnf
file.
If you have a git ignored file (for example a database_config.php
) in your application that normally needs to be edited,
you can define a template file, that reduces further configuration of the Docker user.
At container startup all environment variable occurrences within this file will be set to the corresponding value and copied to the defined location.
To do this, first set a file mapping in your docker-compose.yml
as an environment variable with the prefix TEMPLATE_
:
services:
web:
environment:
TEMPLATE_DB_CONFIG: "database_config.php:./path/to/application/database_config.php"
In _docker/web/templates/
define your configuration template file like this (in this case database_config.php
):
<?php
return [
"database" => [
"host" => '${DOMAIN}',
"user" => '${DATABASE_USER_MAIN}',
"password" => '${DATABASE_PASS_MAIN}',
"database_name" => '${DATABASE_NAME_MAIN}'
],
'wkhtmltopdfBinary' => '${WKHTMLTOPDF_BINARY}'
];
This also supports the bash default syntax with :-
between variable name and default value:
${DOMAIN:-test.local}
If the environment variable DOMAIN
is not set, this will default to test.local
The variables DATABASE_USER_MAIN
and DATABASE_PASS_MAIN
should be set in the _docker/public.env
.
If you have more databases set these variables with a different suffix than _MAIN
:
DATABASE_USER_MAIN="some_user"
DATABASE_PASS_MAIN="s3cr3tP4ssw0rd"
DATABASE_USER_SOMEOTHERSUFFIX="another_user"
DATABASE_PASS_SOMEOTHERSUFFIX="sauce"
In this case the literal environment variables in the file _docker/web/templates/database_config.php
will be replaced
with the corresponding environment value and then the file will be copied to ./path/to/application/database_config.php
.
The resulting file ./path/to/application/database_config.php
can be for example:
<?php
return [
"database" => [
"host" => 'test.local',
"user" => 'some_user',
"password" => 's3cr3tP4ssw0rd',
"database_name" => 'some_db_name'
],
"wkhtmltopdfBinary" => '/usr/bin/wkhtmltopdf'
];
Composer is installed in its latest version if you define it as a build argument in the docker-compose.yml
:
services:
web:
build:
args:
INSTALL_COMPOSER: "true"
It runs composer install
at startup if you set the path where it is executed with the following environment variable in your docker-compose.yml
:
services:
web:
# ...
environment:
COMPOSER_INSTALL_PATHS: ./
If you need to execute composer install
in multiple paths you can do this by separating them by a :
:
COMPOSER_INSTALL_PATHS: ./path:./another/path
If you need another composer version installed (for example 1.x
) you can change this in the Dockerfile
under _docker/web/
:
# install composer
RUN install-php-extensions @composer-1 && apt-get update && apt-get install -y unzip git
More info on this on https://github.com/mlocati/docker-php-extension-installer#installing-composer
Node.js and npm
is installed if you define it as a build argument in the docker-compose.yml
:
services:
web:
build:
args:
INSTALL_NPM: "true"
The same goes for the installation of grunt
with the build argument INSTALL_GRUNT
.
If installed it automatically runs npm install
in the directory you specified with:
services:
web:
# ...
environment:
NPM_INSTALL_PATHS: ./path/to/sub/dir
If you need to execute npm install
in multiple paths you can do this by separating them by a :
:
NPM_INSTALL_PATHS: ./path:./another/path
If you need to access other applications from the outside that run inside the container, you need make the port available on the outside.
For example, if you run a Vue.js application with npm run serve
and this application runs inside the container on http://localhost:8080
,
you can make this available on the outside under http://<DOMAIN>:8080
if you add this to your docker-compose.yml
:
services:
web:
ports:
- "${LOOPBACK_IP:-127.255.255.254}:8080:8080"
If you want to install wkhtmltopdf as a depencency change the docker-compose.yml
to:
services:
web:
build:
args:
INSTALL_WKHTMLTOPDF: "true"
After that you have to rebuild the container (see Note 1)
The binary path is stored in the environment variable WKHTMLTOPDF_BINARY
available in the web
container
If you have to install one or more locales in the web
container, so for example the following PHP function will work... :
setlocale(LC_ALL, 'de_DE.UTF-8');
... you can add it to the web
service as a build argument in the docker-compose.yml
:
services:
web:
build:
args:
INSTALL_LOCALES: "de_DE, fr_FR"
INSTALL_LOCALES
takes a string separated with ,
for multiple locales as input.
If your application has any git submodules (sub repositories) you can automatically update/pull them at startup with changing your docker-compose.yml
to:
services:
web:
environment:
CONTAINS_GIT_SUBMODULES: "true"
This will execute git submodule update --init --recursive
.
This is also executed automatically if you have a .gitmodules
file in your root directory.
To disable, set CONTAINS_GIT_SUBMODULES
to "false"
If you want to automatically add extra needed files, that you normally would copy manually from the live server
and that are available in a public git repository, you can specify the environment variable CLONE_INTO_<SUFFIX>
in the docker-compose.yml
:
services:
web:
environment:
CLONE_INTO_SAMPLE: "./sub-directory:github.com/sample/sample.git" # Warning: git repo link needs to be without 'http://'
When starting the container this will clone the public repository github.com/sample/sample.git
in the background
and copy it's content to ./sub-directory
without the hidden .git
folder.
With Docker you want to match the environment of the server where the application will run later as close as possible. This helps prevent weird errors and bugs that only occur on the live system or only on your development system.
Here are some additional tips to prevent these discrepancies in the first place:
- Put as many files as possible into git where it makes sense. This way other developers do not have to copy single files from the live system
- put
composer.lock
into git, so other developers have the exact same versions when they install dependencies withcomposer install
. - put
.htaccess
to git if possible, so for example redirects are treated the same as on the live system. - use the template files as explained above to automatically configure database configuration files that should not be in git.
- put
- Replicate the
php.ini
configuration from the server as close as possible. You can configure your own.ini
file in_docker/web/additional-inis
. - Install the same PHP version as used on the live system with the
FROM
command in the_docker/web/Dockerfile
- Install the same Composer version as used on the live system.
This can prevent dependency installation problems.
More information on how to install a specific composer version in the
_docker/web/Dockerfile
on here https://github.com/mlocati/docker-php-extension-installer#installing-composer - Install all PHP extensions that are required by the application. This is also done in the
_docker/web/Dockerfile
copy .env.sample
to .env
Set your project name in the .env
file like so:
PROJECT_NAME=my-project
This prevents container name collisions in the future.
Before we can configure a custom domain where your application will be available at,
we need to set an unused loopback IP from the IP range 127.0.0.0/8
in your .env
file.
For example:
LOOPBACK_IP=127.0.0.2
Note: If your configured LOOPBACK_IP
is already in use, it will tell you the next free IP at container startup.
Set a custom domain where your application will be available at:
DOMAIN=test.local
Set the absolute path to your hosts
file on your OS:
HOSTS_FILE=/c/Windows/System32/drivers/etc/hosts # use this for Windows
#HOSTS_FILE=/etc/hosts # use this for Linux
#HOSTS_FILE=/private/etc/hosts # use this for MacOS
This is needed to automatically set your domain in your hosts file.
Important: Make sure your
hosts
file is writable. On Windows this is done like this:
To import a database at initial docker startup move a .sql
file to ./_docker/db/sql
After that map this file to a database name of your choice in your .env
file:
DATABASE_NAME_MAIN=test_name
DATABASE_FILE_MAIN=test.sql
This will import the file ./_docker/db/sql/test.sql
in the newly created database test_name
at container startup within the db
container.
To import multiple databases, add another .sql
file and add more mapping variables with another suffix than _MAIN
like above.
Important: If the container is already running, stop it, tear it down and start it again to trigger the import:
docker-compose down # Warning: this will delete your database inside the container
docker-compose up
If you are on Windows or Mac download and install Docker Desktop if you haven't already.
If you are on Winodws make sure to disable the WSL 2 based engine and use the Hyper-V backend instead as this can lead to performance issues with docker volumes (10x faster).
This can be done in the Docker Desktop Dashboard:
After configuration you can start your containers with executing the following command in the root directory of your project:
docker-compose up
Make sure to start the docker daemon first (Docker Desktop).
The first time executing this takes a few minutes.
Tip: The Dashboard of Docker Desktop can be quite useful to manage your containers.
If you get an error like:
Ports are not available: listen tcp 127.55.0.1:3308: bind: Der Zugriff auf einen Socket war aufgrund der Zugrifssrechte des Sockets unzulässig.
You have to change the corresponding port in you .env
file to a port that is available on your local system. For example:
WEB_PORT: 8081
WEB_PORT_SSL: 4443
DB_PORT: 3309
You can connect to the database (for example DBeaver) with the following credentials:
- host: the
DOMAIN
variable you specified in your.env
file - port: the
DB_PORT
variable you specified in your.env
file - user:
root
- password:
root
(if not specified differently indocker-compose.yml
with the environment variableMYSQL_ROOT_PASSWORD
of thedb
container) - database name: the
DATABASE_FILE_MAIN
orDATABASE_FILE_<OTHER_SUFFIX>
you specified in your.env
file
To open the application frontend open http://<DOMAIN>:<WEB_PORT>
in your browser.
You can configure your DOMAIN
in .env
file. Make sure to restart the containers after changing it:
DOMAIN=test.local
If you need the application available through https, you need to set HTTPS=true
in your .env
file and
create a certificate with the install-cert.cmd <DOMAIN>
script. This script works on Windows, macOS and Linux.
After that, you need to restart your containers and then you can access your application at https://<DOMAIN>:<WEB_PORT_SSL>
.
If you need to access it on Firefox, you first need to import the RootCA certificate in the certificate settings of Firefox (see screenshot).
The install-cert.cmd
tells you the location of this certificate file called rootCA.pem
.
If you want to access your application from another device on the same network, you have to set up a new port mapping
with the IP your computer has on the corresponding network interface (for example 192.168.178.54
).
Do this in a new file called docker-compose.override.yml
in your project root:
services:
web:
ports:
- "192.168.178.54:${WEB_PORT:-80}:80"
After you restarted your container, the application will be available at http://192.168.178.54:<WEB_PORT>
on the network you are connected to.
To disable this again comment out this line and the ports:
line:
services:
web:
#ports:
#- "192.168.178.54:${WEB_PORT:-80}:80"
Xdebug is installed and enabled by default and is ready to use.
To disable xdebug with PHP version < 7.2
change the file ./_docker/web/additional-inis/xdebug.ini
to:
xdebug.remote_enable=0
To disable xdebug with PHP version >= 7.2
change the file ./_docker/web/additional-inis/xdebug.ini
to:
xdebug.mode=off
To enable xdebug with PHP version >= 7.2
change the file ./_docker/web/additional-inis/xdebug.ini
to:
xdebug.mode=debug
After that you need to restart the container.
In this file you can also specify any other xdebug config you might need.
To use xdebug with PHPStorm do the following:
-
Set a breakpoint in your code:
-
Listen to xdebug connections in your IDE:
-
Turn on your browser debug extension and reload page:
-
Choose correct project and accept incoming connection:
-
Set path mappings in IDE settings: root of project should be set to
/var/www/html
-
Reload page
If you fucked up somewhere in between, delete the server configuration in Settings > Languages & Frameworks > PHP > Servers
and start over.
The following picture describes how to setup a Debug Configuration in PHPStorm in Run - Edit Configurations
:
- Click on the
+
to create a new configuration - Choose
PHP Script
- Choose your local file you want to debug
- Click on
...
next to the CLI interpreter - Click on the
+
to create a new PHP CLI Interpreter - Choose
From Docker, Vagrant, VM, WSL, Remote...
- Choose
Docker Compose
- Click on
New...
next to Server if you haven't created a Docker Server Configuration yet. Confirm withOK
. - Make sure to set the Interpreter to the created
web
CLI Interpreter.
After that make sure you also have the PHP CLI Interpreter configured to the docker container at Settings - Languages & Frameworks - PHP
.
Here you also have to configure the path mapping of your project root to the root inside the container (/var/www/html
):
Now you can start debugging your PHP script with...
Warning: this is currently untested for server xdebug versions
>= 3.X
. You may need to adjust some more xdebug variables. In this case, this may help: https://xdebug.org/docs/upgrade_guide
To debug an application from a remote server in your local IDE do the following:
-
Make sure your docker container are running
-
Enable xdebug extension on the server. Here with example from Hetzner:
-
Make sure the xdebug variables are set as follows:
xdebug.remote_enable=1 xdebug.remote_connect_back=0 xdebug.remote_host=localhost xdebug.remote_port=9000 # change to port 9003 on xdebug version >= 3.X (run 'php --version' to find out)
These variables can also be set with an
.htaccess
file in the root directory on the server like:php_flag xdebug.remote_enable On php_flag xdebug.remote_connect_back Off php_value xdebug.remote_host localhost php_value xdebug.remote_port 9000 # change to port 9003 on xdebug version >= 3.X (run 'php --version' to find out)
-
Start an SSH tunnel from your machine to the server and enter the ssh password:
ssh -p <server-ssh-port> -R 9000:<DOMAIN>:9000 <server-ssh-user>@<server-ip>
Note 1:
DOMAIN
is the domain you configured in your.env
file. The other parameters should be available from the provider, where the site is hosted.Note 2: Set the xdebug
9000
to9003
if the xdebug version of the server is>= 3.X
. You can find out which xdebug version the server has by runningphp --version
if the xdebug extension is enabled -
Once the SSH tunnel connection is established, follow the steps from Xdebug usage above.
Note: At step 5 the root of your project is NOT
/var/www/html
. Instead, in the SSH sessioncd
to the root path of the project and runpwd
to get the full absolute path. Enter this path at step 5 as your project root.
Composer, npm and grunt is pre-installed in the web
container.
composer install
and npm install
is automatically executed at container startup if configured.
If you want to manually execute another command, it is best to execute it in the web
container:
./shell.cmd # go into container on unix/linux
shell.cmd # go into container on windows
composer <any-composer-command>
npm <any-npm-command>
grunt
<any-other-command>
In a big application it is likely that there will be emails sent to a user at some point.
To avoid sending emails to users while developing or testing, the web
container catches all emails sent to port 25
,
465
or 587
and sends them to a local mail server called MailHog.
The web
container will catch these outgoing emails, if you enable it in your .env
file:
CATCH_MAIL=true
To disable catching these emails and sending them normally, just comment out or set to false
.
You can look at the catched emails at http://<DOMAIN>:<CATCH_MAIL_PORT>
. The default CATCH_MAIL_PORT
is 8025
.
To go into a container simply run the shell
script with it's two optional parameters.
-
The first parameter is the corresponding docker service name of the container (for example
db
). The default isweb
. -
The second parameter is the shell entry point (for example
/bin/sh
). The default is/bin/bash
.
# on unix/linux:
./shell.cmd # goes into web container
# on windows:
shell.cmd # goes into web container
# on unix/linux:
./shell.cmd db # goes into db container
# on windows:
shell.cmd db # goes into db container
# on unix/linux:
./shell.cmd db /bin/sh # goes into db container on /bin/sh
# on windows:
shell.cmd db /bin/sh # goes into db container on /bin/sh
- testing docker-lampose on linux and MacOS