DockerLocal is used for setting up a LEMP site to be served on localhost:PORT
DockerLocal runs on docker containers - nginx + php-fpm, memcached, redis, mysql/mariadb and your project's web application files. Provides customization for configuring the Dockerfile, nginx, php-fpm, and managing databases.
Note: Your DockerLocal containers can also be used with ProxyLocal for a local DNS of all your DockerLocal projects so that you can run your sites locally at custom domains, like docker.yoursite.com and docker.anothersite.com
Easy Version Setting
The master branch has the latest versions (see defaults below) - but they are configurable in the /versions/
folder.
You can configure the following:
- Ubuntu Version (default 20.04)
- PHP Version (default 8.0)
- Database Image Mysql or Mariadb (default mariadb:10.6)
Note: For convenience, you could also checkout a specific branch eg. PHP-7.4 and use those defaults (or adjust using same technique ^).
- Requirements
- Install
- Commands
- Configuration Files
- Add Database cnf files
- Install NVM-PM2
- How to use Xdebug
- Install ProxyLocal
- Bash 4+ on linux; MacOS default 3.2.57 is okay - recommend using zsh - install via curl
- Docker for Mac (or Docker && Docker-Compose) - tested with Docker version 20.10.0, build 7287ab3
- docker-compose 1.29.2 (not v2 - disable in your terminal with:
docker-compose disable-v2
- because docker-compose v1 uses underscores, not dashes, the scripts depend on)
- docker-compose 1.29.2 (not v2 - disable in your terminal with:
DockerLocal does not support mssql/sqlsvr by default anymore - because it's not compatible with macs using m1 chips (docker does not support).
To enable MsSQL, use a custom Dockerfile. We have created a template you can copy with necessary packages:
cp Dockerfile-template-use-mssql-example Dockerfile-template-custom
Note: You can make any changes you want to the Dockerfile-template-custom - to install different packages, etc.
The following will cover how to install and use DockerLocal:
- Where to clone
- Simple installation examples
Clone DockerLocal into at the same level as your site's html folder
- YourSite
- DockerLocal
- html/index.php
- conf
Assuming the above,
cd ~/code/YourSite
git clone [email protected]:amurrell/DockerLocal.git
Note: You can also use DockerLocal as a git submodule
Control DockerLocal with shell commands. All the following examples rely upon:
- Going to the commands folder.
cd DockerLocal/commands
- Understanding that shell scripts are triggered with a
./
preceeding the command, eg../site-up
- If you get prompted for a password, use your user's password for your computer!
Examples:
- Ex: Basic
- Ex: With specifc port
- Ex: With New Empty Database
- Ex: With Remote-fetched Database
- Ex: With different root path
This will install your site at localhost:3000 with no custom configuration (no database, default port, default webserver's root path eg. html/index.php or html/index.html).
- Run
./site-up
First, shutdown your previously loaded containers with ./site-down
. Skip if you have not ran ./site-up
yet.
Then, use ./site-up -p=XXXX
to specify a different port, eg. localhost:XXXX
Tip: create a port file to maintain this project without having to type the switch -p for every command you run:
./site-down
first, shutdown containers if you already started them on another portcd ../ && echo "3001" > port
this puts a file "port" in DockerLocal foldercd commands && ./site-up
(no need to use -p switch)
Now it's running on your custom port 3001!
Go to localhost:3001
To create a database, you can use the switch -c=DB_NAME
. You only need to run this once.
To edit the database name your DockerLocal containers will use a database configuration file. You can change this, but you will still need to use the -c
switch to create new databases (one time).
./site-up -c=example_local_db
, where switch -c means create database. Run only 1 time to create.
You can safely shutdown and start up again and it remembers you're using that db (in php env vars) - because of that configuration file.
./site-down
to shut it down./site-up
to start up again
This example shows getting an sql dump of a remote database and importing it into a local copy in the mysql container.
Copy the DockerLocal/databases-example.yml
to DockerLocal\databases.yml
and fill it out with your remote host information.
./site-up
If your project's root path for the public website files are in a different folder, you can configure that via the DockerLocal/web-server-root
file.
You can create this file initially with:
./site-up -w=/var/www/site/app/public
The first time you run that, it will create your configuration file. After that, it will only override that file if you pass -w again. To permanently change it, do so in the configuration file.
cd DockerLocal/commands
./site-down
cd ~/vhosts/ProxyLocal/commands
./proxy-down
If you want to check your log files, you can find them in DockerLocal/logs
.
Your queue logs, access.log, php_error_log.log and error.log are all in that folder.
For quick tailing of the logs, you can use the DockerLocal/commands
for ./site-logs
:
Ex: In your code
error_log(json_encode($myObject));
Ex: In your terminal, in DockerLocal/commands
:
./site-logs # All the logs are tailed
./site-logs -p # Only php_error_log is tailed
./site-logs -e # Only error_log is tailed
./site-logs -a # Only access.log is tailed
./site-logs -h # Help to find what the switches are
If you have a nested project that uses nvm & npm to build, you can run ./site-npm
with some options -p=<path>
and -n=<commands>
.
This makes it easier to install & run npm commands from the DockerLocal/commands folder.
## where 'my-app' is a folder at the root of your project.
## inside your container it would be at /var/www/site/my-app
./site-npm -p="my-app" -n="npm install && npm run development"
Configuration Helper: To save you time from having to specify the path all the time, save a file in DockerLocal/app-path
with the path in it. eg. my-app
# from your terminal, at root of project:
echo "my-app" > DockerLocal/app-path
cd DockerLocal/commands
./site-db -c=example_database_name
cd DockerLocal/commands
./site-db -l=example_database_name
(if DockerLocal/database exists, the -l switch is not needed)
- setup databases.yml by copying contents from DockerLocal/databases-example.yml
cd DockerLocal/commands
./site-up
this will create a db-name.sql.remote.dump file, one time.
- ensure databases.yml is up to date
cd DockerLocal/commands
./site-db
-
If you don't know then name of your current database:
cat DockerLocal/database
-
If you want to see all databases you have locally:
cd DockerLocal/commands
&&./site-ssh -h=mysql
&&show databases;
-
If you know the name of your local db then:
cd DockerLocal/commands
./site-db -d=your_db
This generates a file
DockerLocal/data/dumps/your_db.sql.dump
which you may want to rename so you wont write over this from subsequent dumps. -
If you want this dump to include add/drop SQL:
cd DockerLocal/commands
./site-db -d=your_db -a=true
cd DockerLocal/commands
./site-db -f=import-complete.sql
cd DockerLocal/commands
./site-db -i=name_of_local_db -f=import-partial.sql
-
./site-ssh -h=mysql
and you'll be in mysql app as root user of mysql -
./site-ssh -h=mysqlroot
to get into the container as root shell user. Can domysql -u root -p1234
to get into mysql app. -
./site-ssh -h=web
&&cd /var/www/site/
to see your project. Run commands that might create files (ie. php artisan for laravel projects) as this www-data user. -
./site-ssh -h=webroot
to get into the web container as root. Good for looking at confs etccd /etc/php/7.0/fpm/pool.d
for php-fpm conf, orcd /etc/nginx/
for nginx conf -
./site-ssh -h=memcached
.. there's really no reason to be here. -
./site-ssh -h=web -c='cat /etc/passwd' where -c is a command to pass into the web container. Notice the difference between using single quotes. ie
-c="$(whoami)"and
-c='$(whoami)'`.
Requires Ngrok
cd DockerLocal/commands
./site-ngrok
- Download ngrok
- Unzip it to your Applications directory
ln -s /Applications/ngrok /usr/local/bin/ngrok
You can use nvm (at a specific version) and pm2 (globally installed) with DockerLocal - they are already installed via the Dockerfile-Template
.
If a DockerLocal/ecosystem.config.js
file exists, which is a configuration file for pm2, then also the site-up
command will pm2 start
for you.
Additionally, running ./site-nvmpm2
manually will trigger a pm2 restart all
if the ecosystem file exists.
Often times, you need to restart pm2 servers to catch changes to the code that is running inside the queues.
Eg. Laravel jobs will need you to reload workers (or horizon) after making changes to the code. Running ./site-nvmpm2
will pm2 restart all
.
Example: Restart all servers
cd DockerLocal/commands
./site-nvmpm2
Example: Manually access pm2 commands
cd DockerLocal/commands
./site-ssh -h=web
cd ~
pm2 list all
You can use the defaults or choose a different version for:
- ubuntu & php & nodejs & yaml versions in the
Dockerfile-template
. Variables (used in the file) are:- UBUNTU_VERSION
- PHP_VERSION
- NVM_VERSION
- mysql or mariadb version in
docker-compose-custom.yml
for the mysql image. Variable (used in the file) is:- DB_IMAGE
You can see the default configs in DockerLocal/versions
in the form of files:
DockerLocal
- versions
- php-version
> 7.4
- ubuntu-release-name
> focal
- ubuntu-version
> 20.04
- db-image
> mariadb:10.5.8
- nvm-version (nodejs version)
> 16.14.2
- yaml-version
> 2.2.1 (changes to 2.2.3 when php8.1+)
If you cat versions/php-version
you'll see the contents are just the version:
8.0
You can overide any of these by copying the file and renaming to prepend override. Eg.
echo "7.4" > versions/override-php-version
If you have an existing database with data in it using one image (eg. mysql) and you plan to switch to another (eg. mariadb), then you will need to backup your database, remove the docker volume, re-import your data.
Note: This is not an issue if you are changing the version of the same db image type (eg. mariadb:10.5.8 -> mariadb:10.6).
Here are the steps:
-
Get a backup SQL dump of your current setup
./site-db -d=your_db_name
cd DockerLocal/data/dumps
mv
your_db_name.sql.dump
or whatever it's called tobefore_db_image_upgrade.sql
-
Turn off the docker containers if running -
./site-down
. -
Remove the data volume - make sure you have a backup!
[sudo] docker volume ls see which one it is... eg. dockerlocal{PORT}-mysql-data-{PORT} [sudo] docker volume rm <name-of-volume>
-
Make the version change - eg.
echo "mariadb:10.6" > versions/override-db-image
-
Turn back on
./site-up
- then create a db again./site-up -c=your_db_name
-
Import:
./site-db -i=your_db_name -f=before_db_image_upgrade.sql
If you would like to create a default custom port (other than 3000) for all commands in this project, create a port file.
The port in the command refers to localhost:port and it is used in DockerLocal by all the containers (to create their mapped ports and container names).
cd DockerLocal
echo "3001" > port
Of course, you can still override this default by using the -p
switch.
So, your commands would be so simple:
./site-up
./site-db
./site-ssh -h=web
./site-down
The default is root path is /var/www/site/html
, which assumes that you have an index.php
or index.html
file directly in your html
folder.
What if your project was different?
- YourSite
- DockerLocal
- app/public/index.php
- conf
You can create this file initially with:
./site-up -w=/var/www/site/app/public
The first time you run that, it will create your configuration file. After that, it will only override that file if you pass -w again. To permanently change it, do so in the configuration file.
To confirm that your path is loaded correctly, you can check your DockerLocal\nginx.site.computed.conf
file that is generated from running ./site-up
. You should see the root YOURPATH
line in your nginx server block.
Use the file xdebug.custom.ini
to override xdebug.ini
. If you don't have it, create it. Like all overriding/custom configuration, it will not be version controlled.
Your custom file (or the default file if no custom is set) will be computed into xdebug.computed.ini
by the site-up
command and then referenced in the Dockerfile-Template
to add xdebug config to your project.
If you want to turn off xdebug, change the line xdebug.mode=develop,debug
to xdebug.mode=off
in the file DockerLocal/xdebug.custom.ini
(if you don't have that file, create it by copying DockerLocal/xdebug.ini
). After changing this custom file, always run ./site-up
again.
You can find information about how to use xdebug here.
Use this file to override the nginx.site.conf. You can start by copying the nginx.site.conf file and then making your edits.
The nginx.site.computed.conf file is basically the same as your custom, but with variables computed.
The following variables are available to use in your nginx.site.custom.conf file:
- SITE_DOMAIN - this will use SITE_DOMAIN for those running proxylocal, or smartly use WEB_PORT if not
- WEB_SERVER_ROOT - this is your web servers root path. See DockerLocal/web-server-root
After running the -c=DB_NAME
command, you will have a file in DockerLocal/database
with the DB_NAME
in it.
You can change this out by editing the file. To create new databases, you will still use the -c
switch (1 time).
You can use -l switch to specify a different local database than the one saved in DockerLocal/database - this just overrides which one to use, but doesn't edit the file.
To confirm your database
file is being read correctly, you can check the DockerLocal/env-custom.yml
file for your DB_NAME.
To confirm your database
exists, you can ssh into the mysql container: ./site-ssh -h=mysql
and show databases;
If you have a remote database to use as a source to populate your local database, create a databases.yml
- Copy databases-example.yml to databases.yml
Ex:
databases:
host:
user:
pass:
port: 3306
3001: example_com
-
This file is for PHP env vars. Make your own as env.yml if you need to customize it.
-
Can use for local database connection
-
Variables you can use:
DATABASE_NAME
(Will be populated by matching site-port in databases.yml, or-c
and-l
switch values, for creating or using a locally created database)DATABASE_HOST
(relative to web container:mysql
)DATABASE_PORT
(relative to host machine: your localhost:port + 3306, eg.6307
if port is 3001)
See the output of your env vars in php7-fpm.site.custom.conf
Ex:
envs:
DL_DB_NAME: DATABASE_NAME
DL_DB_USER: root
DL_DB_PASS: 1234
DL_DB_HOST: mysql
DL_DB_PORT: 3306
DL_DB_LOCAL_PORT: DATABASE_PORT
php7 conf:
env[DL_DB_NAME]="example_com"
env[DL_DB_USER]="root"
env[DL_DB_PASS]="1234"
env[DL_DB_HOST]="mysql"
env[DL_DB_PORT]="3306"
env[DL_DB_LOCAL_PORT]="6307"
This is a template file, for the outputted php7-fpm.site.custom.conf
.
Ensure you keep ;ENV
in your template for env vars to populate there. The rest is yours to modify!
NOTE: If you modify this file, it will look like unstaged file changes.
TODO: change php7-fpm.site.custom.conf to be a computed file, and allow for edits to get stored in the custom file.)
The "Web" container is defined by this Dockerfile-template
.
- it contains variables to help with versioning:
UBUNTU_VERSION
andPHP_VERSION
- it relies on the
./site-up
script to compute the variables to create an untracked file:Dockerfile-computed
- this is what gets used by docker-compose.
If you need to install any other php libraries or modify this template beyond the version overrides, feel free to create a copy, edit and save as Dockerfile-template-custom
, which will get used over Dockerfile-template
.
This file is a special configuration file for pm2, a process manager and npm package.
PM2 is useful for projects that have workers, running on queues using CLI - typically asynchronous. An example is laravel worker queues for jobs (or running them through laravel horizon).
PM2 is included with DockerLocal via the nvm-pm2.sh
script that is ran by both the Dockerfile-Template
&& site-up
command after the docker containers are booted.
- The first call installs nvm at the
nvm-version
(node js version). It also installs pm2 globablly. - The second call, from
site-up
willpm2 start
if you have aDockerLocal/ecosystem.config.js
- You can manually call it any time with
./site-nvmpm2
in theDockerLocal/commands
folder, which will basicallypm2 restart all
, if ecosystem.config.js is present.
To access commands directly, see next section, Accessing pm2 directly.
For help with the contents of ecosystem.config.js, see:
- PM2 + Laravel Horizon
- PM2 + Laravel Horizon + XDebug
- Perform your own google searches, since this is a configuration for PM2 specifically.
You can site-ssh
to access pm2 commands directly.
./site-ssh -h=web
cd ~
pm2 list all
If you are planning to use pm2 to run horizon, here's an example configuration for that:
{
name: "laravel-horizon",
cwd: "./site/app",
interpreter: "php",
script_path: "/var/www/site/app/artisan",
script: "artisan",
args: "horizon",
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: "1G",
merge_logs: true,
out_file: "/var/www/site/DockerLocal/logs/horizon.log",
error_file: "/var/www/site/DockerLocal/logs/horizon.log",
log_date_format: "MM/DD/YYYY HH:mm:ss",
}
If you are wanting to use xdebug to monitor CLI running queue workers eg. in laravel via horizon, I caution that horizon runs every second which may become a very difficult testing environment... but it can be done. Here's an example ecosystem file entry to accomplish running xdebug on horizon via pm2.
{
name: "xdebug-horizon",
cwd: "./site/app",
interpreter: "php",
script_path: "/var/www/site/app/artisan",
script: "artisan",
args: "-dxdebug.remote_autostart=1 -dxdebug.remote_host=host.docker.internal -dxdebug.remote_port=9000 -dxdebug.remote_enable=1 horizon",
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: "1G",
merge_logs: true,
out_file: "/var/www/site/DockerLocal/logs/horizon.log",
error_file: "/var/www/site/DockerLocal/logs/horizon.log",
log_date_format: "MM/DD/YYYY HH:mm:ss",
},
You can use pm2 with this project, but it's a bit more manual.
Look at the documentation for the configuration file DockerLocal/ecosystem.config.js for more information on how it install nvm-pm2 and setup config.
If you were tangentally looking how to install nvm on your local machine (not in the container), and do not want to use brew, here's how:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
. ~/.nvm/nvm.sh
. ~/.profile
. ~/.bashrc
. ~/.zshrc
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
command -v nvm
If you need to adjust settings eg. my.cnf
for the database (mysql|mariadb) image, you can add *.cnf
config files to DockerLocal/data/custom/
.
Note: options.cnf
is already in version control, but you can create any other files there and have them ignored - eg. my.cnf
How does it work? In the docker-compose
, there's a shared volume which connects these files to the container's /etc/mysql/conf.d
:
volumes:
...
- ./data/custom:/etc/mysql/conf.d
...
Xdebug is a debugging tool that communicates with your IDE while listening to incoming requests to your application. It does all this on another localhost:<port>
and with configuration. This means:
- your web application & server will need necessary packages - eg. php-xdebug.
- your web application / server will need necessary configuration - eg. in xdebug.ini (custom configuration available in xdebug.custom.ini)
- your IDE will need an extension installed so that the debugger and the IDE can communicate
- your IDE may need configuration on how to connect to your debugger - eg. launch.json in vscode
So the first two items on the list are solved by DockerLocal by default!
-
The
Dockerfile-template
has installed the php-xdebug package for your version. -
It also added the
xdebug.computed.ini
to appropriate location in the container. If you want to change settings, read about using custom configuration file xdebug.custom.ini, which includes how to disable xdebug.
To use xdebug with vscode & DockerLocal, simply:
-
Install the xdebugging extension for your IDE - eg. vscode needs a PHP X-debug extension if using a php debugger. eg. extension id: xdebug.php-debug
-
If you don't yet have a hidden
.vscode
folder ("hidden" is indicated by leading.
) - add one to your root repo<your-project>/.vscode
so that it looks something like this:- your-project - .vscode - DockerLocal - html - your-code
-
add a
launch.json
file into this.vscode
folder, or add just the configuration entry if you have a launch.json file already.Example:
launch.json
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Listen for Xdebug", "type": "php", "request": "launch", "port": 9003, "pathMappings": { "/var/www/site": "${workspaceFolder}" } } ] }
Explanations of the configuration:
- The
/var/www/site
in"/var/www/site": "${workspaceFolder}"
references the path to your running application within DockerLocal web container - where php is running.cd DockerLocal/commands && ./site-ssh -h=web
and you are in that same directory. - The
${workspaceFolder}
portion references the IDE vscode's folder for the project.
- The
-
If you want to add
extensions.json
in your.vscode
folder, so that they can be recommended to anyone who runs your code:{ "recommendations": [ "xdebug.php-debug" ] }
So once everything is setup, go to your debugger tab in vscode (looks like a play button with bug over it).
In the top left - it says "Run and debug" with a dropdown. In the dropdown, use the "Listen For Xdebug" which is going to mirror what you wrote in your launch.json if that is setup correctly.
Click the Play button. Now some controls appear centered at the top of vscode. It is "listening". Go trigger requests - visit your website, hit postman, whatever to hit your site.
It will stop at any break points and let you look at variables. You can step back, step forward, or click deeper into functions.
It's easy to see the xdebug logs due to the configuration setup in Dockerfile-template
- we put the logs at /var/www/site/DockerLocal/logs/xdebug.log
You can use the DockerLocal command ./site-logs -x
to tail the x debug log as a shortcut for (tail -f xdebug.log)
Note that it will say Could not connect to debugging client.
when you are not running the debugger in your IDE. Simply start a debug session and it will connect when you hit your project.
ProxyLocal - Sets up your hosts file + reverse proxy to access site on localhost:port by domain.
Steps (one time thing):
- git clone ProxyLocal in vhosts or equivalent so that it is at the same level as your sites
- create sites.yml from sites-example.yml, try:
3001: docker.example.com
- go to ProxyLocal/commands and run ./proxy-up command
- do normal DockerLocal setup; now can use
./site-up -n=docker.example.com
or./site-up
if you have a port file in your DockerLocal that matches an entry in sites.yml
Commands:
git clone [email protected]:amurrell/ProxyLocal.git
cd ProxyLocal/commands
# remember to make the sites.yml in ProxyLocal!
./proxy-up
The reverse proxy is running: localhost
While this file lives in ProxyLocal's repo, it helps the site commands by allowing -n=docker.example.com
to be specified rather than a specific port.
This is great if you plan to run many sites with DockerLocal and don't want to remember each site's port.
Ex.
sites:
3001: docker.example.com
By adding a port: site
to sites.yml, you can go to your DockerLocal project and do ./site-up -p=port
or if you have a port file ./site-up
and it will automatically boot up ProxyLocal if not already running.
Tip: ProxyLocal handles nginx conf enabling - if you have to do it manually you can do cd ProxyLocal/commands && ./proxy-nginx -p=3001
to enable nginx conf for site at port 3001 and ./proxy-nginx -p=3001 -d=true
to disable it.
You can use one databases.yml in ProxyLocal like this if all your data is at the same remote host, with same user/pass combo:
databases:
host:
user:
pass:
port: 3306
3001: example_com
3002: another_example_com
3003: yet_another_example_com
Where the databases example_come, another_example_com, etc are remote and can all be accessed by the same host, user, pass, and port. If you need specifics, you can still create a databases.yml per site and keep it in DockerLocal.
The Numbered Keys in that yaml represent the localhost:PORT and therefore corresponding mysql container to import the remote db into: dockerlocal3001-mysql-1