Every time I have a new computer whether it be a MacBook or PC (PopOS). I have to install MAMP / XAMP with some brew installations, without a clue where all those files being installed. Sometimes you want to run different PHP version depending on a project or perhaps you want to revive legacy ones, run https SSL locally because WebRTC only works secured environment or even attach a Node project with socket file.
And you don't want to pollute your machine with development files unless containerized.

Docker is my man!

For this guide I let you install some Docker containers, working with Nginx configurations, connect containers, run docker commands, have SSL Certificates on your localhost.

  • Alpine
  • NGINX
  • PHP FPM 5.6.23 / 7.0.8
  • MariaDB (MySQL)
  • Phpmyadmin
- You can skip the whole article and scroll down to download from my github repository.
- This article is expects you already have Docker installed on your machine and have root privileges and know some terminal commands. This article is base on a PopOS/Debian computer.
- Tip: Post-installation steps for Linux to run docker without sudo

Let’s create some files

Create docker-compose.yml

version: '2'

services:
  nginx:
    image: nginx:alpine
    restart: always
    links:
      - fpm5-6-23
      - fpm7-0-8
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/enabled:/etc/nginx/conf.d
      - ./nginx/snippets:/nginx/snippets
      - ./nginx/certificates:/nginx/certificates
    volumes_from:
      - data

  fpm5-6-23:
    image: php:5.6.23-fpm-alpine
    restart: always
    volumes_from:
      - data

  fpm7-0-8:
    image: php:7.0.8-fpm-alpine
    restart: always
    volumes_from:
      - data

  db:
    image: harianto/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./db/storage:/var/lib/mysql

  dbadmin:
    image: phpmyadmin/phpmyadmin
    restart: always
    depends_on:
      - db
    # ports:
    #   - "8080:80"

  data:
    image: alpine:latest
    command: echo "READY!!!"
    volumes:
      - ./vhosts:/vhosts
      - ./tmp:/tmp
file: /var/docker/docker-xnmp-vhosts/docker-compose.yml

Then run command from /var/docker/docker-xnmp-vhosts/ directory.

# Start docker containers from compose file
docker-compose up
Run commands from: /var/docker/docker-xnmp-vhosts/
You can also run this as background process: docker-compose up -d

From here if you check volumes some directories are created, and currently the port 80 and 443 are available from localhost. http://localhost

Upon visiting the url, there's actually nothing to see except an 404 error page

Create simple page for Default vhosts

<!DOCTYPE html>  
<html>  
<head>  
	<title>DEFAULT DOMAIN</title>
    <style>
        body {
            background: black;
            color: hotpink;
        }
    </style>
</head>
<body>
	This is default domain
</body>
</html>
file: /var/docker/docker-xnmp-vhosts/vhosts/_default_/httpdocs/index.html

Create Nginx Default configuration

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	server_name _;

	index index.html index.htm;
	root /vhosts/_default_/httpdocs;
	location / {
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
		try_files $uri $uri/ /index.php$is_args$args =404;
	}
}
file: /var/docker/docker-xnmp-vhosts/nginx/enabled/_default_.conf

Restart NGINX

Sinds we know our docker containers are still running, we can run the command from
/var/docker/docker-xnmp-vhosts/ directory:

# Restart Nginx
docker-compose exec nginx nginx -t && docker-compose restart nginx
Run commands from: /var/docker/docker-xnmp-vhosts/
This command will use already running nginx container and execute a command nginx -t for testing valid configuration and restart only nginx container.
nginx - is the service name we gave in the docker-compose.yml file.
nginx -t - is the command that are available in the nginx container.
Every changes you make in the .conf file. You need to restart your nginx server to take effect.

After web server restart we can visit the page: http://localhost

Virtual Hosts (VHOSTS)

Let’s create a file structure for example.com with two sub-domains in mind for example: antique and beauty.

/var/docker/docker-xnmp-vhosts/
  nginx/
    enabled/example.com.conf
  vhosts/
    example.com/
      httpdocs/index.html
      subdomains/
        antique/httpdocs/index.html
        beauty/httpdocs/index.html
File Structure
After good configuration, you can add as many subdomains as you wish without the need to restart Nginx

Vhost: example.com

Virtual #1 - example.com

<!DOCTYPE html>  
<html>  
<head>  
	<title>Example</title>
    <style>
        body {
            background: lightgrey;
            color: cadetblue;
        }
    </style>
</head>
<body>
	Example domain
</body>
</html>
file: /var/docker/docker-xnmp-vhosts/vhosts/example.com/httpdocs/index.html

Virtual #2 - antique.example.com

<!DOCTYPE html>  
<html>  
<head>  
	<title>Antique Example</title>
    <style>
        body {
            background: lightslategrey;
            color: lightblue;
        }
    </style>
</head>
<body>
	Antique subdomain
</body>
</html>
file: /var/docker/docker-xnmp-vhosts/vhosts/example.com/subdomains/antique/httpdocs/index.html

Virtual #3 - beauty.example.com

<!DOCTYPE html>  
<html>  
<head>  
	<title>Beauty Example</title>
    <style>
        body {
            background: lightslategrey;
            color: lightblue;
        }
    </style>
</head>
<body>
	Beauty subdomain
</body>
</html>
file: /var/docker/docker-xnmp-vhosts/vhosts/example.com/subdomains/beauty/httpdocs/index.html

Nginx configuration: example.com

# domain: example.com
server {
	disable_symlinks off;
	server_name ~^example\.com(\.localhost)?$;

	root /vhosts/example.com/httpdocs;

	autoindex on;
	index index.html index.php;

	location / {
		try_files $uri $uri/ /index.php$is_args$args =404;
	}
}

# subdomains: *.example.com
server {
	disable_symlinks off;
	server_name ~^((?<subdomain>.*)\.)example\.com(\.localhost)?$;

	root /vhosts/example.com/subdomains/${subdomain}/httpdocs;

	autoindex on;
	index index.html index.php;

	location / {
		try_files $uri $uri/ /index.php$is_args$args =404;
	}
}
file: /var/docker/docker-xnmp-vhosts/nginx/enabled/example.com.conf
Nginx advanced configuration that takes regular expression-like.
To make * wildcard work, subdomain will be used for ${subdomain}

Restart Nginx to apply new configurations and visit to see:
- example.com.localhost
- antique.example.com.localhost
- beauty.example.com.localhost

Configuring NGINX for PHP Projects

/var/docker/docker-xnmp-vhosts/
  nginx/
    enabled/
      php5.conf
      php7.conf
    snippets/
      php5.6.23-fpm.conf
      php7.0.8-fpm.conf
  vhosts/
    php5/httpdocs/index.php
    php7/httpdocs/index.php
File Structure

PHP 5 & 7

Create simple phpinfo files in the vhosts directory

<?php phpinfo()
file: /var/docker/docker-xnmp-vhosts/vhosts/php5/httpdocs/index.php
<?php phpinfo()
file: /var/docker/docker-xnmp-vhosts/vhosts/php7/httpdocs/index.php

Create reusable snippets

#location ~ \.php(/|$) {
	try_files      $uri = 404;
	fastcgi_index  index.php;
	fastcgi_split_path_info ^(.+\.php)(/.*)$;
	include fastcgi_params;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	fastcgi_param HTTPS on;
	fastcgi_buffers 16 16k;
	fastcgi_buffer_size 32k;
#}
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/snippet-php-fastcgi.conf
location ~ \.php(/|$) {
	include /nginx/snippets/snippet-php-fastcgi.conf;
	fastcgi_pass fpm5-6-23:9000;
}
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/php5.6.23-fpm.conf
location ~ \.php(/|$) {
	include /nginx/snippets/snippet-php-fastcgi.conf;
	fastcgi_pass fpm7-0-8:9000;
}
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/php7.0.8-fpm.conf

Apply reusable snippets for domains

server {
	disable_symlinks off;
	server_name ~^php5(\.localhost)?$;
	root /vhosts/php5/httpdocs;
	autoindex on;
	index index.html index.php;

	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php5.6.23-fpm.conf;
}
file: /var/docker/docker-xnmp-vhosts/nginx/enabled/php5.conf
server {
	disable_symlinks off;
	server_name ~^php7(\.localhost)?$;
	root /vhosts/php7/httpdocs;
	autoindex on;
	index index.html index.php;

	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php7.0.8-fpm.conf;
}
file: /var/docker/docker-xnmp-vhosts/nginx/enabled/php7.conf

Restart Nginx to apply new configurations and visit to see:
- php5.localhost
- php7.localhost

Revive legacy projects with PHP extensions

When you found out that your old website uses Mcrypt, your page shows PHP errors and simple docker container isn’t enough that would force you to install some extensions.

/var/docker/docker-xnmp-vhosts/
  docker-compose.yml
  dockerfiles/php-7-0-8-fpm-ext-alpine.Dockerfile
  nginx/
    enabled/php7-ext.conf
    snippets/php7.0.8-fpm-ext.conf
  vhosts/
    php7-ext/httpdocs/index.php
File Structure

Add legacy project in Virtual Host directory

<?php phpinfo()
file: /var/docker/docker-xnmp-vhosts/vhosts/php7-ext/httpdocs/index.php

Create Nginx snippet and domain config

location ~ \.php(/|$) {
	include /nginx/snippets/snippet-php-fastcgi.conf;
	fastcgi_pass fpm-ext7-0-8:9000;
}
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/php7.0.8-fpm-ext.conf
server {
	disable_symlinks off;
	server_name ~^php7-ext(\.localhost)?$;
	root /vhosts/php7-ext/httpdocs;
	autoindex on;
	index index.html index.php;

	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php7.0.8-fpm-ext.conf;
}
file: /var/docker/docker-xnmp-vhosts/nginx/enabled/php7-ext.conf

Create custom Dockerfile with php extensions

Luckily you can create your own Dockerfile and use that in your  docker-compose.yml.

FROM php:7.0.8-fpm-alpine

RUN apk add --no-cache --update \
        libmcrypt \
        libmcrypt-dev \
    && docker-php-ext-install \
        mysqli \
        opcache \
        intl \
        sockets \
        mcrypt \
    && rm -rf /tmp/* /var/cache/apk/* \
    && echo "=============================================" \
    && php -m
file: /var/docker/docker-xnmp-vhosts/dockerfiles/php-7-0-8-fpm-ext-alpine.Dockerfile
php:7.0.8-fpm-alpine is docker image it build from. You can choose whichever version you wish, but the installation script may change, for example mcrypt on version 7.1.x.

Apply PHP with extension Dockerfile in the docker-compose.yml.

Add new service: fpm-ext7-0-8

services:
  fpm-ext7-0-8:
    build:
      context: ./dockerfiles
      dockerfile: php-7-0-8-fpm-ext-alpine.Dockerfile
    restart: always
    volumes_from:
      - data
New service: fpm-ext7-0-8, snippet: docker-compose.yml 
Snippet

Link Nginx with the new service

services:
  nginx:
    links:
      - fpm-ext7-0-8
Linked service fpm-ext7-0-8, snippet: docker-compose.yml
Snippet

Full configuration

version: '2'

services:
  nginx:
    image: nginx:alpine
    restart: always
    links:
      - fpm5-6-23
      - fpm7-0-8
      - fpm-ext7-0-8
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/enabled:/etc/nginx/conf.d
      - ./nginx/snippets:/nginx/snippets
    volumes_from:
      - data

  fpm5-6-23:
    image: php:5.6.23-fpm-alpine
    restart: always
    volumes_from:
      - data

  fpm7-0-8:
    image: php:7.0.8-fpm-alpine
    restart: always
    volumes_from:
      - data

  fpm-ext7-0-8:
    build:
      context: ./dockerfiles
      dockerfile: php-7-0-8-fpm-ext-alpine.Dockerfile
    restart: always
    volumes_from:
      - data

  db:
    image: harianto/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./db/storage:/var/lib/mysql

  dbadmin:
    image: phpmyadmin/phpmyadmin
    restart: always
    depends_on:
      - db
    # ports:
    #   - "8080:80"

  data:
    image: alpine:latest
    command: echo "READY!!!"
    volumes:
      - ./vhosts:/vhosts
      - ./tmp:/tmp
file: /var/docker/docker-xnmp-vhosts/docker-compose.yml

This time we only need to full restart the docker containers to take effect.

If you have a running terminal and you used: docker-compose up you can press:
Ctrl+C to close program

# Check running containers
docker-compose ps

# Kill containers when you use: docker-compose up -d
docker-compose down

# Start containers as background
docker-compose up -d
Run commands from: /var/docker/docker-xnmp-vhosts/

A symbolic link for example to the latest version php configuration file.

/var/docker/docker-xnmp-vhosts/
  nginx/
    snippets/php-fpm-default.conf
File Structure

For example: ln -nsf <source> <target>

# Create php-fpm-default.conf symlink for later use
ln -nsf php7.0.8-fpm-ext.conf php-fpm-default.conf
Run commands from: /var/docker/docker-xnmp-vhosts/nginx/snippets/

Manage MySQL Database with dockerized Phpmyadmin

Since we already have a mysql-database and phpmyadmin-webapp in our docker-compose.yml. We only need to config Nginx and point to it.

/var/docker/docker-xnmp-vhosts/
  nginx/
    enabled/dbadmin.conf
    snippets/snippet-server-location-upstream.conf
File Structure

Nginx configuration for Phpmyadmin

# server {
#     server_name ~^dbadmin(\.localhost)?$;
#     resolver 127.0.0.11 valid=30s;
#     set $upstream http://dbadmin:80;
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass $upstream;
    }
# }
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/snippet-server-location-upstream.conf
server {
    server_name ~^dbadmin(\.localhost)?$;
    resolver 127.0.0.11 valid=30s;
    set $upstream http://dbadmin:80;
    include /nginx/snippets/snippet-server-location-upstream.conf;
}
file: /var/docker/docker-xnmp-vhosts/nginx/enabled/dbadmin.conf

Restart Nginx to apply new configurations and visit to see:
- dbadmin.localhost

username: root, password: root
To change database root password edit docker-compose.yml and look for MYSQL_ROOT_PASSWORD

Configure Self-signed Certificates SSL for development

/var/docker/docker-xnmp-vhosts/
  nginx/
    bin/
      createDomainDirectory.sh
      createLocalhost.sh
      createNginxSslConfigFileExample.sh
      createRootCA.sh
      dns.txt.example
      recreateNginxSslConfigFileExample.sh
      README.md
    enabled/ssl.conf
    snippets/
      snippet-ssl.conf
      ssl-defaultserver.conf
      ssl-domain.conf
  vhosts/
    ssl/
      httpdocs/index.php
      subdomains/
      	test/httpdocs/index.php
File Structure

Post-install SSL for Debian/Ubuntu

When you run command (when you followed and finish this SSL article)  
curl https://ssl.localhost would work perfectly fine when in you install certificates on your system, but visiting that URL in Chrome will not. You’ll get security error page. On Mac wouldn't have that problem because those browsers uses the system Trust Store.

You can tell applications (such as Chrome / Firefox) that use NSS for its certificate management to use the system Trust Store.

sudo apt-get update && sudo apt-get install -y p11-kit libnss3
find / -type f -name "libnssckbi.so" 2>/dev/null | while read line; do
    sudo mv $line ${line}.bak
    sudo ln -s /usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-trust.so $line
done
There seem to be more versions of libnssckbi.so out there than just in libnss3. The following is a script to find them all, back them up, and replace them with links to p11-kit
Found this script from Superuser
Guide to add self-generated root certificate authorities for 8 operating systems and browsers

Create SSL simple pages for vhosts

<?php phpinfo()
file: /var/docker/docker-xnmp-vhosts/vhosts/ssl/httpdocs/index.php
<?php phpinfo()
file: /var/docker/docker-xnmp-vhosts/vhosts/ssl/subdomains/test/httpdocs/index.php

Create SSL Snippets for Nginx

# ssl                 on;
ssl_certificate     /nginx/certificates/localhost/localhost.crt;
ssl_certificate_key /nginx/certificates/localhost/localhost.key;
ssl_session_timeout  5m;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA';
ssl_prefer_server_ciphers on;
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/snippet-ssl.conf
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
include /nginx/snippets/snippet-ssl.conf;
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/ssl-defaultserver.conf
listen 443 ssl;
listen [::]:443 ssl;
include /nginx/snippets/snippet-ssl.conf;
file: /var/docker/docker-xnmp-vhosts/nginx/snippets/ssl-domain.conf

Optional: ssl.conf

This Nginx config file will be generated by the bin script
server {
	server_name ~^ssl(\.localhost)?$;
	return 301 https://$host$request_uri;
}

server {
    include /nginx/snippets/ssl-domain.conf;
	server_name ~^ssl(\.localhost)?$;

	index index.html index.php;
    root /vhosts/ssl/httpdocs;
	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php-fpm-default.conf;
}

# SUBDOMAINS
server {
	server_name ~^((?<subdomain>.*)\.)ssl(\.localhost)?$;
	return 301 https://$host$request_uri;
}

server {
	include /nginx/snippets/ssl-domain.conf;
	server_name ~^((?<subdomain>.*)\.)ssl(\.localhost)?$;

	index index.html index.php;
	root /vhosts/ssl/subdomains/${subdomain}/httpdocs;
	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php-fpm-default.conf;
}
file: /var/docker/docker-xnmp-vhosts/nginx/enabled/ssl.conf

Scripts to create SSL Certificates for localhost development

Skip this part if you want to download script files and run single command.

dns.txt

localhost
*.localhost
*.example.com.localhost
example.com.localhost
php5.localhost
*.php5.localhost
php7.localhost
*.php7.localhost
php7-ext.localhost
*.php7-ext.localhost
ssl.localhost
*.ssl.localhost
file: /var/docker/docker-xnmp-vhosts/nginx/bin/dns.txt

createDomainDirectory.sh

#!/bin/bash
CERT_DIR=../certificates

if [[ $# -eq 0 ]] ; then
    echo -e 'Error Script: Need argument\n\n\tEXAMPLE: ./createDomainDirectory.sh localhost'
    exit 0
fi


OUTPUT=`cat <<EOF
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
IP.1 = 10.10.10.20
IP.2 = 127.0.0.1
EOF
`
# OUTPUT: EOF

# Import dns.txt file if exist, else use dns.txt.example and make newline as a array and sort
DNSTXT=dns.txt
[ ! -f "$DNSTXT" ] && DNSTXT=dns.txt.example
IFS=$'\r\n' GLOBIGNORE='*' command eval 'DOMAINS=($(sort <"$DNSTXT"))'; unset IFS

for DNS in "${DOMAINS[@]}"
{
    (( COUNT++ ))
	OUTPUT="${OUTPUT}\nDNS.$COUNT = $DNS"
}
echo -e "$OUTPUT"
echo -e "$OUTPUT" > domains.ext

# Create Dir
mkdir -p $CERT_DIR/$1
# Generate Certificates and Keys for Domain
openssl req -new -nodes -newkey rsa:2048 -keyout $CERT_DIR/$1/$1.key -out $CERT_DIR/$1/$1.csr -subj "/CN=localhost"
openssl x509 -req -sha256 -days 1024 -in $CERT_DIR/$1/$1.csr -CA $CERT_DIR/RootCA.pem -CAkey $CERT_DIR/RootCA.key -CAcreateserial -extfile domains.ext -out $CERT_DIR/$1/$1.crt
file: /var/docker/docker-xnmp-vhosts/nginx/bin/createDomainDirectory.sh

createLocalhost.sh

#!/bin/bash
./createDomainDirectory.sh localhost
file: /var/docker/docker-xnmp-vhosts/nginx/bin/createLocalhost.sh

createRootCA.sh

#!/bin/bash
CERT_DIR=../certificates
openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout $CERT_DIR/RootCA.key -out $CERT_DIR/RootCA.pem -subj "/C=NL/O=XNMP/CN=XNMP-Root-CA"
openssl x509 -outform pem -in $CERT_DIR/RootCA.pem -out $CERT_DIR/RootCA.crt

COMMAND_CP_CA_CERTIFICATES_TO_SYSTEM="cp $CERT_DIR/RootCA.crt /usr/local/share/ca-certificates && update-ca-certificates -f"
if [[ -d /usr/local/share/ca-certificates ]]; then
   [[ $EUID -ne 0 ]] && sudo bash -c "$COMMAND_CP_CA_CERTIFICATES_TO_SYSTEM" || "$COMMAND_CP_CA_CERTIFICATES_TO_SYSTEM"
   echo "#--- RootCA.crt copied to /usr/local/share/ca-certificates and Updated"
fi
file: /var/docker/docker-xnmp-vhosts/nginx/bin/createRootCA.sh

createNginxSslConfigFileExample.sh

#!/bin/bash
CERT_DIR=../certificates
ENABLED_DIR=../enabled

[ -f "$ENABLED_DIR/ssl.conf" ] && echo -e "Error Script: $ENABLED_DIR/ssl.conf already exist!" && exit 0
# RootCA
[[ ! -f "$CERT_DIR/RootCA.pem" || ! -f "$CERT_DIR/RootCA.key" ]] && ./createRootCA.sh && echo RootCA CREATED
# localhost
[[ ! -f "$CERT_DIR/localhost/localhost.crt" || ! -f "$CERT_DIR/localhost/localhost.key" ]] && ./createLocalhost.sh localhost && echo localhost.* CREATED

OUTPUT=`cat <<'EOF'
server {
	server_name ~^ssl(\.localhost)?$;
	return 301 https://$host$request_uri;
}

server {
    include /nginx/snippets/ssl-domain.conf;
	server_name ~^ssl(\.localhost)?$;

	index index.html index.php;
    root /vhosts/ssl/httpdocs;
	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php-fpm-default.conf;
}

# SUBDOMAINS
server {
	server_name ~^((?<subdomain>.*)\.)ssl(\.localhost)?$;
	return 301 https://$host$request_uri;
}

server {
	include /nginx/snippets/ssl-domain.conf;
	server_name ~^((?<subdomain>.*)\.)ssl(\.localhost)?$;

	index index.html index.php;
	root /vhosts/ssl/subdomains/${subdomain}/httpdocs;
	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php-fpm-default.conf;
}
EOF
`
# OUTPUT: EOF

echo -e "$OUTPUT" > $ENABLED_DIR/ssl.conf
echo DONE.
file: /var/docker/docker-xnmp-vhosts/nginx/bin/createNginxSslConfigFileExample.sh

recreateNginxSslConfigFileExample.sh

#!/bin/bash
CERT_DIR=../certificates
ENABLED_DIR=../enabled

# [ -f "$ENABLED_DIR/ssl.conf" ] && echo -e "Error Script: $ENABLED_DIR/ssl.conf already exist!" && exit 0
# RootCA
./createRootCA.sh && echo RootCA CREATED
# localhost
./createLocalhost.sh localhost && echo "localhost.(crt|key|csr) CREATED"

OUTPUT=`cat <<'EOF'
server {
	server_name ~^ssl(\.localhost)?$;
	return 301 https://$host$request_uri;
}

server {
    include /nginx/snippets/ssl-domain.conf;
	server_name ~^ssl(\.localhost)?$;

	index index.html index.php;
    root /vhosts/ssl/httpdocs;
	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php-fpm-default.conf;
}

# SUBDOMAINS
server {
	server_name ~^((?<subdomain>.*)\.)ssl(\.localhost)?$;
	return 301 https://$host$request_uri;
}

server {
	include /nginx/snippets/ssl-domain.conf;
	server_name ~^((?<subdomain>.*)\.)ssl(\.localhost)?$;

	index index.html index.php;
	root /vhosts/ssl/subdomains/${subdomain}/httpdocs;
	location / {
		try_files $uri $uri/ /index.php$is_args$args;
	}

	include /nginx/snippets/php-fpm-default.conf;
}
EOF
`
# OUTPUT: EOF

echo -e "$OUTPUT" > $ENABLED_DIR/ssl.conf
echo DONE.
file: /var/docker/docker-xnmp-vhosts/nginx/bin/recreateNginxSslConfigFileExample.sh

Download bin.zip

The source inside you’ll find above information

Download script files and extract them to nginx/bin folder, shown above, then run:

./createNginxSslConfigFileExample.sh
Run script from: /var/docker/docker-xnmp-vhosts/nginx/bin
Certificates will be generated in this folder: /var/docker/docker-xnmp-vhosts/nginx/certificates

Restart Nginx to apply new configurations and visit to see:
- ssl.localhost

How to use the bin files

  • When you want update or create SSL certificates for new (sub)domains you can append newline in dns.txt. For example:
ghost.localhost
*.ghost.localhost
snippet: /var/docker/docker-xnmp-vhosts/nginx/bin/dns.txt

Then run to update all your certificates:

./createLocalhost.sh
Run script from: /var/docker/docker-xnmp-vhosts/nginx/bin

Restart Nginx to apply new configurations and visit to see:
- ghost.localhost
- blog.ghost.localhost

  • When you want to renew SSL RootCA Certificate, then run:
./createRootCA.sh
./createLocalhost.sh
Run script from: /var/docker/docker-xnmp-vhosts/nginx/bin
Install your generated RootCA.pem in your system, browser or device, follow guide from: Install Root Certificates
You might need to restart your computer to take effect

Restart Nginx to apply new configurations.

  • When you want to start over again and regenerate the files, then run:
./recreateNginxSslConfigFileExample.sh
Run script from: /var/docker/docker-xnmp-vhosts/nginx/bin

Restart Nginx to apply new configurations.

Share network

You can share network, by append this snippet below.

# create network: docker network create xnmp-network
networks:
  default:
    external:
      name: xnmp-network
snippet: /var/docker/docker-xnmp-vhosts/docker-compose.yml

Then create a network xnmp-network

# Run once
docker network create xnmp-network
You can with other docker-compose.yml together in shared network.
(work in progress)

Sources

GitHub:  Cross-Platform (X), Nginx (N), MariaDB (M), PHP (P)