This repository provides a Docker-powered local development environment for Dockerized projects, compatible with macOS and Linux.
It includes a set of docker-compose files and Træfik configuration with SSL support, backed by mkcert, to enable running a local network with custom DNS provided by dnsmasq. This setup facilitates developing microservices locally with access outside the Docker network.
- Key Features
- Purpose
- Benefits for Team
- Requirements
- Quick Start Guide (macOS)
- Quick Start Guide (Linux)
- Connecting your Projects to Shared Services
- Example: Spin-up Laravel Sail Project
- Example: Want to See a Ready-Made Template?
- Security Policy
- Want to Contribute?
- Contributors
- Social Links
- License
- Automated Local DNS and SSL Setup: Eliminates manual edits to
/etc/hosts
and self-signed certificate warnings. - Consistent Development Environment: Uniform setup for all team members, reducing environment-related bugs.
- Elimination of Port Conflicts: Traefik handles port management, allowing multiple dockerized projects to run concurrently.
- User-Friendly Local URLs: Access projects via custom local domains like
project.docker
instead oflocalhost:8000
. - Simplified CORS and Cookie Management: SSL support for local domains mirrors production settings.
- Enhanced Testing Environment: Test OAuth, secure cookies, and HTTPS APIs locally.
- Improved Service Discovery and Routing: Traefik automates service discovery and routing within your Docker network.
- Ease of Integration with Existing Projects: Connect your existing Docker projects to this setup effortlessly.
This project simplifies local development by addressing common issues such as:
- Frequent updates to the
/etc/hosts
file. - Requirement of administrative access for changes.
- Lack of SSL support for custom domains.
- Port conflicts when forwarding Docker service ports to the host machine.
- Use of cumbersome hostnames like
localhost:8800
for local projects. - Complex CORS setup and Cookie configuration.
For Linux and macOS users, dnsmasq eliminates the need to edit the hosts file for each project, providing a streamlined solution when used with Traefik, mkcert, and Docker. This repository configures Traefik to work with dnsmasq, offering a system-wide DNS solution for microservices and enabling DNS and SSL features with local domains.
Integrating this Docker Shared Services project into your team's tech stack can significantly enhance your development workflow. This setup is compatible with a wide range of HTTP-based projects, including backend frameworks like Laravel, Symfony, or Spiral, frontend frameworks, and any other services that run in Docker and communicate over HTTP.
By standardizing the local network setup across different machines, your team can:
- Maintain consistency.
- Reduce configuration work.
- Resolve port conflicts between multiple Docker services.
- Provide SSL support for local domains.
- Work with CORS and Cookies in a scenario close to production.
- Set up OAuth providers to work with custom local domains.
This ensures smoother collaboration and boosts overall productivity.
If you like/use this project, please consider ⭐️ starring it. Thanks!
- macOS Monterey+ or Linux
- Docker 26.0 or newer
- Installed mkcert binary in system
- See full installation instructions in their official README.md
- Quick installation on macOS:
brew install mkcert nss
Installed and configured dnsmasq daemonCan be installed and configured automatically via our ansible-role-dnsmasq ansible role- DNSMasq service now is shipped and configured with this repository.
-
Install Homebrew (if not installed):
If Homebrew is not already installed, run the following command:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-
Install Docker (if not installed):
Set up Docker Desktop via Homebrew:
brew install --cask docker
-
Install
mkcert
andnss
:mkcert
is a tool that creates locally-trusted development certificates, andnss
provides support for mkcert certificates in Firefox.brew install mkcert nss
-
Create shared project directory:
This repository should be run once per machine, so let's create a shared directory for this project:
mkdir -p ~/projects/infra && cd ~/projects/infra
-
Clone this repository:
git clone \ [email protected]:wayofdev/docker-shared-services.git \ ~/projects/infra/docker-shared-services && \ cd ~/projects/infra/docker-shared-services
-
Create
.env
file:Generate a default
.env
file, which contains configuration settings for the project.make env
Open this file and read the notes inside to make any necessary changes to fit your setup.
-
Install root certificate and generate default project certs:
This step installs the root certificate into your system's trust store and generates default SSL certificates for your local domains, which are listed in the
.env
file, under the variableTLS_DOMAINS
.make cert-install
[!WARNING]
Currently, on macOS, you may need to enter your password several times to allow
mkcert
to install the root certificate. This is a one-time operation and details can be found in this upstream GitHub issue. -
Run this project:
Start the Docker services defined in the repository.
make up
-
Check that all Docker services are running:
Ensure Docker is running and services are up by using the
make ps
andmake logs
commands.make ps make logs
-
Add custom DNS resolver to your system:
This allows macOS to understand that
*.docker
domains should be resolved by a custom resolver via127.0.0.1
, where our DNSMasq, which runs inside Docker, will handle all DNS requests.sudo sh -c 'echo "nameserver 127.0.0.1" > /etc/resolver/docker' sudo dscacheutil -flushcache sudo killall -HUP mDNSResponder
You can check that DNS was added by running:
scutil --dns
Example output:
resolver #8 domain : docker nameserver[0] : 127.0.0.1 flags : Request A records, Request AAAA records reach : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
[!NOTE]
Instead of creating the
/etc/resolver/docker
file, you can add127.0.0.1
to your macOS DNS Servers in your Ethernet or Wi-Fi settings.Go to System Preferences → Network → Wi-Fi → Details → DNS and add
127.0.0.1
as the first DNS server.This allows you to do it one time, and if you need to create a new local domain, for example
*.mac
, in the future, it will be automatically resolved without creating a separate/etc/resolver/mac
file. -
Ping
router.docker
to check if DNS is working:Ensure that the DNS setup is functioning correctly.
ping router.docker -c 3 ping any-domain.docker -c 3
-
Access Traefik dashboard:
Open https://router.docker.
You should see the Traefik Dashboard:
At this point, you should have a working local development environment with DNS and SSL support ready to be used with your projects.
Services will be running under a shared Docker network called network.ss
, and all projects or microservices that will share the same Docker network will be visible to Traefik. The local DNS, served by DNSMasq, will be available on *.docker
domains.
In this section, we'll walk through setting up the docker-shared-services
project on an Ubuntu distribution. While the steps are specific to Ubuntu, they should be adaptable to other Linux distributions with minor modifications.
-
Install Docker: Easiest and quickest way to get started is to install Docker Desktop for Linux.
-
Install
certutil
:sudo apt update sudo apt install libnss3-tools
-
Install
mkcert
:curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64" chmod +x mkcert-v*-linux-amd64 sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
More detailed instructions can be found in the
mkcert README.md
. -
Create shared project directory:
mkdir -p ~/projects/infra && cd ~/projects/infra
-
Clone this repository:
git clone \ [email protected]:wayofdev/docker-shared-services.git \ ~/projects/infra/docker-shared-services && \ cd ~/projects/infra/docker-shared-services
-
Create
.env
file:Generate a default
.env
file, which contains configuration settings for the project.make env
Open this file and read the notes inside to make any necessary changes to fit your setup.
-
Install root certificate and generate default project certs:
make cert-install
-
Disable stub DNS listener:
To prevent conflicts with the DNSMasq service, disable the stub DNS listener in the
systemd-resolved
service.sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf sudo systemctl restart systemd-resolved
-
Edit /etc/resolv.conf:
Update the
/etc/resolv.conf
file to use the local DNS server.echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf > /dev/null
Editing
/etc/resolv.conf
directly is not recommended for persistent changes since it is often managed by other services (like Netplan or NetworkManager). However, for a temporary change, you can use: -
Run this project:
Start the Docker services defined in the repository.
make up
-
Check that all Docker services are running:
Ensure Docker is running and services are up by using the
make ps
andmake logs
commands.make ps make logs
-
Ping
router.docker
to check if DNS is working:Ensure that the DNS setup is functioning correctly.
ping router.docker -c 3 ping any-domain.docker -c 3
-
Access Traefik dashboard:
Open https://router.docker.
You should see the Traefik Dashboard:
At this point, you should have a working local development environment with DNS and SSL support ready to be used with your projects.
Services will be running under a shared Docker network called network.ss
, and all projects or microservices that will share the same Docker network will be visible to Traefik. The local DNS, served by DNSMasq, will be available on *.docker
domains.
To connect your projects to the shared services, configure your project's docker-compose.yaml
file to connect to the shared network and Traefik.
This project comes with an example Portainer service, which also starts by default with the make up
command. You can check the docker-compose.yaml
to see how Traefik labels and the shared network are used to spin up Portainer on the https://ui.docker host, which supports SSL by default.
Your project should use the shared Docker network network.ss
and Traefik labels to expose services to the outside world.
-
Change your project's
docker-compose.yaml
file:--- services: web: image: wayofdev/nginx:k8s-alpine-latest restart: on-failure + networks: + - default + - shared volumes: - ./app:/app:rw,cached + labels: + - traefik.enable=true + - traefik.http.routers.api-my-project-secure.rule=Host(`api.my-project.docker`) + - traefik.http.routers.api-my-project-secure.entrypoints=websecure + - traefik.http.routers.api-my-project-secure.tls=true + - traefik.http.services.api-my-project-secure.loadbalancer.server.port=8880 + - traefik.docker.network=network.ss networks: + shared: + external: true + name: network.ss + default: + name: project.my-project
In this configuration, we added the shared network and Traefik labels to the web service. These labels help Traefik route the traffic to the service based on the specified rules.
Replace
my-project
with your preferred project name. -
Generate SSL certs for your project:
Go to the
docker-shared-services
directory:cd ~/projects/infra/docker-shared-services
Edit the
.env
file to add your custom domain:nano .env
Add
*.my-project.docker
to end ofTLS_DOMAINS
variable:TLS_DOMAINS="ui.docker router.docker *.my-project.docker"
Generate SSL certificates and reload
docker-shared-services
:make cert-install restart
Let's walk through an example of setting up a Laravel project using Sail and integrating it with the docker-shared-services
.
-
Create an example Laravel project based on Sail:
curl -s "https://laravel.build/example-app" | bash
-
Open the
docker-compose.yaml
file of theexample-app
project and make adjustments:services: laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.3 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' image: sail-8.3/app - extra_hosts: - - 'host.docker.internal:host-gateway' ports: - - '${APP_PORT:-80}:80' - - '${VITE_PORT:-5173}:${VITE_PORT:-5173}' networks: - sail + - shared depends_on: - ... + labels: + - traefik.enable=true + - traefik.http.routers.test-laravel-app-secure.rule=Host(`api.laravel-app.docker`) + - traefik.http.routers.test-laravel-app-secure.entrypoints=websecure + - traefik.http.routers.test-laravel-app-secure.tls=true + - traefik.http.services.test-laravel-app-secure.loadbalancer.server.port=80 + - traefik.docker.network=network.ss mailpit: image: 'axllent/mailpit:latest' networks: - sail + - shared ports: - - '${FORWARD_MAILPIT_PORT:-1025}:1025' - - '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025' + labels: + - traefik.enable=true + - traefik.http.routers.mail-laravel-app-secure.rule=Host(`mail.laravel-app.docker`) + - traefik.http.routers.mail-laravel-app-secure.entrypoints=websecure + - traefik.http.routers.mail-laravel-app-secure.tls=true + - traefik.http.services.mail-laravel-app-secure.loadbalancer.server.port=8025 + - traefik.docker.network=network.ss networks: sail: driver: bridge + shared: + external: true + name: network.ss
These changes connect the Laravel app and Mailpit docker services to the shared network and expose them via Traefik.
-
Run the Laravel project:
Navigate to the
example-app
directory and start the services using Sail../vendor/bin/sail up -d
-
Check Traefik routers:
Open https://router.docker/dashboard/#/http/routers and check that there are two routers:
Host(api.laravel-app.docker)
→test-laravel-app-secure@docker
Host(mail.laravel-app.docker)
→mail-laravel-app-secure@docker
-
Check the setup:
Ensure that your Laravel application and Mailpit services are running correctly by accessing their respective domains:
- Laravel app — https://api.laravel-app.docker
- Mailpit — https://mail.laravel-app.docker
At this point, your Laravel project is integrated with the wayofdev/docker-shared-services
, utilizing DNS and SSL support for local development.
If you come from the PHP or Laravel world, or if you want to see how a complete project can be integrated with docker-shared-services
, check out wayofdev/laravel-starter-tpl.
This Dockerized Laravel starter template works seamlessly with wayofdev/docker-shared-services, providing a foundation with integrated DNS and SSL support, and can show you the way
to implement patterns, stated in this article, in your projects.
This project has a security policy.
Thank you for considering contributing to the wayofdev community! We are open to all kinds of contributions. If you want to:
- 🤔 Suggest a feature
- 🐛 Report an issue
- 📖 Improve documentation
- 👨💻 Contribute to the code
You are more than welcome. Before contributing, kindly check our contribution guidelines.
- Twitter: Follow our organization @wayofdev and the author @wlotyp.
- Discord: Join our community on Discord.