To develop a web project¹ on a local machine, we usually launch a web-server listening at http://localhost:port.
In production, the url for this project is https://my-app.com.
We want to mimic the production environment on the local computer. How can we have a custom url and HTTPS, like https://my-app.local?
In this article, we are going to:
- Create custom urls for several web projects, like
- Set a reverse-proxy to route the requests to these urls to specific Docker containers.
- Add HTTPS with self-signed certificates.
Set a reverse proxy
We want to route requests to custom urls (like
my-app.local) to specific web-servers. This is the role of a reverse proxy.
We are going to use nginx as a reverse-proxy. We could manually setup and configure nginx, but this has some downside:
- The need to modify the nginx config every time a website is added or removed.
- The need to restart nginx after each config modification.
- The need to expose a port of each container to the host, and therefore keep track of the used ports (two containers can not use the same port).
To avoid these downsides, the magic Docker service
jwilder/nginx-proxy (by Jason Wilder) automates the creation of nginx configs and reloads the proxy server when a container starts and stops. And it has HTTPS support.
nginx-proxy directory next to the the web project directories. On my computer, I have a
Sites directory in my user root where all my projects are stored with a
nginx-proxy directory, create a
docker-compose.yml file and a
certs directory. This structure looks like this:
| +-- docker-compose.yml
| +-- certs
Configure the nginx-proxy Docker service
docker-compose.yml file, paste this content:
This files creates a single Docker service called
nginx-proxy which uses the
jwilder/nginx-proxy image and share its ports
443 with the host. This service belongs to a docker-network called
nginx-proxy. Before starting this service, we need to create the network.
Create the Docker network
In the Terminal:
docker network create nginx-proxy
Start the service
In the Terminal:
docker-compose up -d
This uses the
docker-compose.yml file to launch the
nginx-proxy service which creates and starts a container.
The nginx-proxy is now running and listens on ports 80 and 443 (for HTTPS). If the computer reboots, the nginx-proxy will start automatically with Docker for Mac. Now, we can forget about it: there is no need to change anything in the above configuration to link a new web project.
Configure a custom url for each web project
To make a web project work with a custom url, we need to:
- Make its url point to
- Add its Docker service to the
Make an url point to localhost
To have a custom url like
my-app.local pointing to
localhost, we need to modifiy the
/etc/host file. In the terminal, edit the
hosts file with the
sudo nano /etc/hosts
Navigate with the arrows to the end of the file and add the line:
y to save and exit nano. Now, the custom url points to localhost.
We still need to tell the nginx proxy to route a request to this url to a specific Docker container.
Link a web project to the nginx-proxy
We have a web project with a Docker configuration (like this one for example).
To link this project to the running nginx-proxy, we need to update its own
docker-compose.yml file (not the one from nginx-proxy above) with a few instructions:
1. Environment variables
VIRTUAL_PORT is the port the web server is listening to.
2. Expose ports
The exposed port value is the same as the
Also, we should remove any existing
PORTS instruction as we don’t want to share the ports outside the network.
Now lets start the service with:
$ cd /my-app.local
$ docker-compose up
The app is now listening at
Create a self-signed SSL certificate for a custom domain
To create the certificates, we use the create-ssl-certificate command line tool (by Christian Alfoni). First we install it globally with
npm i -g create-ssl-certificate.
- go to
- issue a certificate with
create-ssl-certificate --hostname my-app --domain local.
- Rename the files
Trust the certificate
- Add the
my-app.local.crtfile to the Keychain Access app.
- In Keychain Access, click on the certificate name to open a popup.
- In this popup, click on the small arrow in front of
When using this certificate, select
Always trustand close the popup.
Now Chrome trusts the certificate. Firefox is a bit more picky and we have to explicitly trust the certificate when prompted.
Finally our app is listening at
In a previous article, I went through the steps to do something similar on an online server. This is useful to replicate our local configuration on a production environment.
- web project: a generic word for web-app, website, web-server… You name it.