There are many options out there for local development for websites, but each one has limitations ranging from not flexible enough to overly complex. The type of projects, or scale of the projects, will help you determine what solution you need.
On nearly every project, I'm utilizing multiple technologies that sometimes are best to run in separate servers (containers):
- Laravel (PHP Backend)
- MySQL (Relational Data)
- MongoDB (Non-Relational Data)
There are several great options for dev environments using this tech stack:
- Laravel Herd (Up and running fast!)
- Laravel Sail (low-config docker setup)
- Custom Docker
What I want to describe today is the custom docker setup I was introduced to a few years back, that still works great today. It allows for custom local domains, multiple 'servers' (aka containers), multiple databases or a centralized database across multiple projects, and more.
You can kind of view Docker as a localized data center, with the ability to 'spin up' pretty much any server you might need: Web server, db server, caching server, load balancer.. you name it.
For this post, we will assume the following conditions are needed:
- A Centralized DB accessible from multiple containers (E.G. You have multiple laravel sites on different containers needing the same database).
- A reverse-proxy setup to allow for local domains to run and easily access multiple sites/services at once.
A couple final things to note to keep in mind moving forward:
The defined external networks in these config files must be manually created via docker and are not automatically created by the files we cover. To keep this guide short, please refer to the docker documentation here (https://docs.docker.com/engine/reference/commandline/network_create/) to create the necessary networks (namely, the mysqlnet network that shares the mysql central database between containers and the proxynet network that shares the traefik routing).
The first container we will configure will be dual purposed: it will host our centralized database and our primary Traefik configuration for the proxy server. This is the common thread that links and enabled all of our future containers.
First, we need to create the local directory to host the files. I placed mine in
~/Sites/docker-common. It can be called anything you want, just whatever makes sense to you. See the image below for what the directory structure will look like when we are finished.
Let's create the structure we saw above. Create the certs and tools directories. Within tools, create the traefik directory. With the root (~/Sites/docker-common in my case), create the docker-compose.yml file. This is the config file that docker uses to create the container.
Generate a local certificate (I utilized
mkcert) to enable https on your local dev sites, and place it in
~/Sites/docker-common/certs. We will then access it in the config file at
/etc/certs and place the cert and key files in their correct place:
Now we will configure Traefik itself. This is the tool where the magic happens! It will handle the localhost domains / ports for you so you don't have to worry about running multiple sites at once.
Yep, it's that easy! Traefik has very in-depth documentation, so your best bet for learning more about it is to visit their docs: https://doc.traefik.io/traefik/
Now, when you start your docker-common container, it will launch the container that runs traefik as well as your database.
Now, you just need to create a container for your actual project. For this example, we will create a directory at ~/Sites/project-x and place a docker-compose.yml file inside. For this project, we will support:
- Connecting to the centralized database.
- Hosting a local MongoDB server (though this could be setup in docker-common).
- Accessing Project X at a custom localhost domain.
First, we need to setup the docker config file:
Next, we can setup some config files for it to use while building to enable extra extensions. We will place them in the ~/Sites/project-x/docker directory. You can use different config files to tell docker how you want different areas configured as it creates those services.
Within the docker directory, we will create a
php directory and place a
Dockerfile in it:
Within the docker directory, we will create an
These two configurations are defined in our docker-compose.yml file to further detail out how we want docker to create our container and services, including which php extensions we need.
One thing that can be confusing is connecting to the central database from another container. You can no longer use 'localhost' or '127.0.0.1' to connect, because that request will look to the container it is coming from, not the centralized one we created (docker-common). What we will do instead is utilize our shared network with the docker-common container to call on the hostname of the mysql service we setup there (in this case, 'db'). Example below.
That's it! Once you build and start the docker containers (I recommend installing / utilizing Docker Desktop), your site should be available at https://projectx.localhost! Have another project that needs to utilize that central database? Just follow the same steps as you did for Project X! The only other change you might need to make (depending on how you generated your SSL Certificate) is you might need a new SSL Certificate in the docker-common to cover the new project's domain.
Credit goes to Adam Dear for creating this structure in the first place, and allowing me to share it here.