DEV Community

Cover image for Generate server block (virtual hosts) for nginx dynamically
Hiram
Hiram

Posted on

Generate server block (virtual hosts) for nginx dynamically

Hey pals, I will show you how to create a basic shell script so you can dynamically generate nginx virtual hosts or "subdomains" for every site you want to put online. There are two examples, one for your node or whatever app that runs locally, the second is for static websites.

Reversed proxy subdomain example

If you are running a node app on a specific port this basic example will help you to create and connect your site. The following is the stub file we will use subdomain.stub:

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

        index index.html index.htm;

        server_name {{DOMAIN}}.yoursite.com;

        location / {
                proxy_pass http://localhost:{{PORT}};
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
        }
}
Enter fullscreen mode Exit fullscreen mode

Now let's create the generator.sh shell script:

#!/bin/bash

SED=$(which sed)
CURRENT_DIR=$(dirname $0)

# check the domain is valid!
PATTERN="^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$"
if [[ "$1" =~ $PATTERN ]]; then
  DOMAIN=$(echo $1 | tr '[A-Z]' '[a-z]')
  echo "Creating hosting for:" $DOMAIN
else
  echo "invalid domain name"
  exit 1
fi

CONFIG="$CURRENT_DIR/$DOMAIN.yoursite.com"
cp $CURRENT_DIR/subdomain.stub $CONFIG
$SED -i "s/{{DOMAIN}}/$DOMAIN/g" $CONFIG
$SED -i "s/{{PORT}}/$2/g" $CONFIG

echo "The subdomain has been successfully generated"

cp $CONFIG "/etc/nginx/sites-available"
ln -s "/etc/nginx/sites-available/$DOMAIN.yoursite.com" "/etc/nginx/sites-enabled"

echo "The subdomain has been moved to nginx to sites-available and symlinked to sites-enabled"
Enter fullscreen mode Exit fullscreen mode

In your terminal you run the command as follows: ./generator.sh DOMAIN PORT

In order to run this script you have to make it executable with this command: chmod u+x generator.sh otherwise you won't be able to execute it.

Now it's ready to run, let me explain what it does:

  • Stores SED as a variable
  • Stores the current location
  • Creates a domain pattern and the compares it with the 1st parameter (DOMAIN)
  • Defines CONFIG location, then clones the subdomain.stub file into the previous definition
  • Replaces the {{DOMAIN}} value inside the cloned configuration
  • Replaces the {{PORT}} value inside the cloned configuration

At this point our virtual host is ready to be published

  • Copies the new virtual host file into "/etc/nginx/sites-available" which is the folder for every available site in nginx
  • Then creates a symlink from the previous location to /etc/nginx/sites-enabled which is the folder for every enabled site in nginx

Once this is done you need to ensure virtual hosts definitions are valid. You can do this by running nginx -t in your terminal. If everything went well now you only need to reload/restart nginx so changes can be applied. nginx service reload/restart.

Static site subdomain example

This example is for static websites (not SSR)

subdmain.stub:

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

        root /var/www/{{DOMAIN}};
        index index.html index.htm index.nginx-debian.html;

        server_name {{DOMAIN}}.yoursite.com;

        location / {
                try_files $uri $uri/ /index.html = 404; #This line redirects to index for correct react router usage
        }
}
Enter fullscreen mode Exit fullscreen mode

Then we use basically the same generator.sh script than before:

#!/bin/bash

SED=$(which sed)
CURRENT_DIR=$(dirname $0)

# check the domain is valid!
PATTERN="^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$"
if [[ "$1" =~ $PATTERN ]]; then
  DOMAIN=$(echo $1 | tr '[A-Z]' '[a-z]')
  echo "Creating hosting for:" $DOMAIN
else
  echo "invalid domain name"
  exit 1
fi

CONFIG="$CURRENT_DIR/$DOMAIN.yoursite.com"
cp $CURRENT_DIR/subdomain.stub $CONFIG
$SED -i "s/{{DOMAIN}}/$DOMAIN/g" $CONFIG

echo "The subdomain has been successfully generated"

cp $CONFIG "/etc/nginx/sites-available"
ln -s "/etc/nginx/sites-available/$DOMAIN.yoursite.com" "/etc/nginx/sites-enabled"

echo "The subdomain has been moved to nginx to sites-available and symlinked to sites-enabled"
Enter fullscreen mode Exit fullscreen mode

The only difference is that we no longer require a PORT, so you will run the command like this: ./generator.sh ${DOMAIN} (Do not forget to make the script executable)

From here the sky is the limit my friend, you could easily create your own stubs/snippets with many variables as needed. Also they only contain the minimum requirements to be served by nginx, have fun adding more specifications.

BONUS!! 🤯

If you read my previous post Easy node apps deployment with PM2 let me show you how you could integrate this virtual host generator to a pm2 deployment set up file ecosystem.config.js:

module.exports = {
  deploy: {
    production: {
      user: USER,
      host: HOST,
      ref: BRANCH,
      repo: REPO,
      path: PATH,
      'post-setup': `npm install && cd setup/ && chmod u+x generator.sh && ./generator.sh ${DOMAIN}`
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Now you only have to create a /setup folder inside your project with the subdomain stub as well as the generator script and that's it. Ready to be executed after setup.

Discussion (1)

Collapse
optimbro profile image
Rahul

Good stuff man, thanks for sharing.