Generate server block (virtual hosts) for nginx dynamically
Tags: nginx, pm2, devops
Published: 2022-01-17 22:01:56

---
title: Generate server block (virtual hosts) for nginx dynamically
published: true
description: Easy guide for nginx sites (domains) generation
tags: nginx, pm2, devops,
cover_image: https://www.cloudsavvyit.com/p/uploads/2017/07/7950f98f.png?height=200p&trim=2,2,2,2
---

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;
        }
}
```

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"
```

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](https://www.geeksforgeeks.org/sed-command-in-linux-unix-with-examples/) 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
        }
}
```

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"
```

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](https://dev.to/eichgi/easy-node-app-deployment-with-pm2-4mpc) 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}`
    }
  }
};
```

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.