A Docker Compose-based Nginx reverse proxy with SSL/HTTPS support using mkcert and dnsmasq DNS server for local development.
- β SSL/HTTPS Support - Trusted local certificates using mkcert
- β Independent Domains - example1.test, example2.test, etc.
- β DNS Server - Local domain resolution via dnsmasq
- β Docker Network - Shared network for inter-service communication
- β HTTP/2 Support - Latest protocol support
- β Auto Redirect - HTTP β HTTPS
project/
βββ proxy/ # Reverse proxy server
β βββ docker-compose.yml # Proxy container definition
β βββ nginx/
β β βββ nginx.conf # Nginx main config
β β βββ conf.d/
β β βββ example1.test.conf # example1.test virtual host
β β βββ example2.test.conf # example2.test virtual host
β βββ certs/ # SSL certificates (mkcert)
β β βββ example1.test.pem
β β βββ example1.test-key.pem
β β βββ example2.test.pem
β β βββ example2.test-key.pem
β βββ dnsmasq/
β βββ dnsmasq.conf # DNS server config
βββ backend1/ # Backend service 1
β βββ docker-compose.yml
β βββ html/
β βββ index.html
βββ backend2/ # Backend service 2
βββ docker-compose.yml
βββ html/
βββ index.html
# macOS
brew install mkcert
# Linux (Ubuntu/Debian)
sudo apt install libnss3-tools
wget -O mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64
chmod +x mkcert
sudo mv mkcert /usr/local/bin/
# Windows
choco install mkcertmkcert -installmkdir -p proxy/{nginx/conf.d,certs,dnsmasq} backend1/html backend2/htmlcd proxy/certs
# Use TRUST_STORES to skip Firefox/Java errors
TRUST_STORES=system mkcert example1.test
TRUST_STORES=system mkcert example2.test
# Verify generated files
ls -la *.pemsudo mkdir -p /etc/resolver
echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/test# Edit /etc/hosts
sudo nano /etc/hosts
# Add these lines
127.0.0.1 example1.test
127.0.0.1 example2.testcd proxy
docker-compose up -d# Backend 1
cd ../backend1
echo '<h1>Backend 1 - HTTPS Works! π</h1>' > html/index.html
docker-compose up -d
# Backend 2
cd ../backend2
echo '<h1>Backend 2 - HTTPS Works! π</h1>' > html/index.html
docker-compose up -dOpen in browser:
If local dnsmasq or another DNS server is using port 53:
# Check port 53 usage
sudo lsof -i :53
# Check Homebrew services
brew services list
# Stop Homebrew dnsmasq
sudo brew services stop dnsmasq
# Restart Docker dnsmasq
cd proxy
docker-compose restart dnsmasqThe docker-compose.yml already includes platform: linux/amd64 setting for automatic handling.
If Firefox/Java related errors occur:
# Use TRUST_STORES environment variable
TRUST_STORES=system mkcert example1.test# Test DNS
nslookup example1.test 127.0.0.1
dig @127.0.0.1 example1.test
# Flush DNS cache
# macOS
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
# Linux
sudo systemd-resolve --flush-caches
# Windows
ipconfig /flushdnsdocker psRunning containers should include:
- nginx-proxy
- dnsmasq
- backend1-nginx
- backend2-nginx
# All logs
docker-compose logs -f
# Specific service logs
docker logs nginx-proxy
docker logs dnsmasqcurl -I --http2 https://example1.testResponse header should show HTTP/2 200!
# Display certificate info
openssl s_client -connect example1.test:443 -servername example1.test < /dev/null | openssl x509 -noout -text
# Check certificate expiration
openssl s_client -connect example1.test:443 -servername example1.test 2>/dev/null | openssl x509 -noout -datesdocker exec nginx-proxy nginx -t# Restart proxy only
cd proxy
docker-compose restart
# Reload Nginx config (zero downtime)
docker exec nginx-proxy nginx -s reload
# Restart all services
docker-compose restart# Stop proxy
cd proxy
docker-compose down
# Stop backends
cd ../backend1 && docker-compose down
cd ../backend2 && docker-compose down# Stop and remove all containers
cd proxy && docker-compose down
cd ../backend1 && docker-compose down
cd ../backend2 && docker-compose down
# Remove network
docker network rm proxy-networkcd proxy/certs
TRUST_STORES=system mkcert example3.testcd ../nginx/conf.d
cp example1.test.conf example3.test.confEdit example3.test.conf:
# HTTP -> HTTPS redirect
server {
listen 80;
server_name example3.test;
return 301 https://$server_name$request_uri;
}
# HTTPS
server {
listen 443 ssl;
http2 on;
server_name example3.test;
ssl_certificate /etc/nginx/certs/example3.test.pem;
ssl_certificate_key /etc/nginx/certs/example3.test-key.pem;
access_log /var/log/nginx/example3.test.access.log;
error_log /var/log/nginx/example3.test.error.log;
location / {
proxy_pass http://backend3-nginx:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}# Add to /etc/hosts
sudo nano /etc/hosts
# 127.0.0.1 example3.testmkdir -p ../../backend3/html
cd ../../backend3Create docker-compose.yml:
services:
backend3-nginx:
image: nginx:alpine
container_name: backend3-nginx
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
- proxy-network
restart: unless-stopped
networks:
proxy-network:
external: true# Start backend service
echo '<h1>Backend 3</h1>' > html/index.html
docker-compose up -d
# Restart proxy
cd ../proxy
docker-compose restartcurl -I --http2 https://example3.testupstream backend_servers {
server backend1-nginx:80;
server backend2-nginx:80;
}
server {
location / {
proxy_pass http://backend_servers;
}
}limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location / {
limit_req zone=mylimit burst=20;
proxy_pass http://backend1-nginx:80;
}
}location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}Issues and pull requests are always welcome!
MIT License