Here's a script I wrote for raising up a basic Nginx server environment.

The script includes an internal script named csb (create Sblock) which is used to create Sblocks in a comfortable way. As I'm new to Linux, I might have had about 2-3 mistakes, especially in that internal script.

#!/bin/bash -x
function nginx {

    apt-get update -y && apt-get upgrade -y
    apt-get install unattended-upgrades tree zip unzip -y
    dpkg-reconfigure --priority=low unattended-upgrades
    mkdir /root/backups /root/backups/db /root/backups/dirs

    ufw app list # Orient your firewall for Nginx.
    cd /usr/src && rm -fv csf.tgz
    wget https://download.configserver.com/csf.tgz && tar -xzf csf.tgz
    cd csf && sh install.sh
    sed -i 's/TESTING = "1"/TESTING = "0"/g' /etc/csf/csf.conf
    csf -r && perl /usr/local/csf/bin/csftest.pl

    cd /usr/local/src && wget http://www.rfxn.com/downloads/maldetect-current.tar.gz && tar -xzf maldetect-current.tar.gz
    cd maldetect-* && bash ./install.sh && cd ~

    # systemctl stats/stop/start/restart/reload/disable/enable # disable/enable --- at boot. # nginx -t
    apt-get install nginx mysql-server php-fpm php-mysql
    sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /etc/php/7.0/fpm/php.ini
    sed -i 's/server_names_hash_bucket_size ..;/server_names_hash_bucket_size ..;/g' /etc/nginx/nginx.conf
    restart php7.0-fpm
    # http://ip_address

    cat <<-'CSB' > /etc/nginx/sites-available/csb
        for domain; do
        > "/etc/nginx/sites-available/${domain}.conf" cat <<EOF
        server {
            listen 80;
            listen [::]:80;
            root /var/www/html/${domain};
            index index.php index.html index.htm index.nginx-debian.html;
                server_name ${domain} www.${domain};

            location / {
                try_files $uri $uri/ =404;

            location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.0-fpm.sock;

            location ~ /\.ht {
                deny all;
    ln -s /etc/nginx/sites-available/${domain} /etc/nginx/sites-enabled/
        systemctl restart nginx.service
    chmod +x /etc/nginx/sites-available/csb

    cat <<-'BASHRC' > /etc/bash.bashrc
    alias csb="bash /etc/nginx/sites-available/csb"

} nginx

Note: For some reason I can't indent the code under ln, here in Code Review.


1 Answer 1


Error handling

The script misses error handling at several places. The downloads could fail or be corrupt, but the script will continue until the end, and finding the failure point can be difficult. Worse, if a failure is quiet enough, it might not even get noticed.

Checking the exit code of every single command can be tedious. An alternative is to add this near the top of the script:

set -euo pipefail

This will cause the script abort on the first failure, and also treat suspicious operations as failure.

Long lines

Long lines are harder to read. Instead of this:

cd /usr/local/src && wget http://www.rfxn.com/downloads/maldetect-current.tar.gz && tar -xzf maldetect-current.tar.gz

It would be easier to read with one line per command:

cd /usr/local/src \
  && wget http://www.rfxn.com/downloads/maldetect-current.tar.gz \
  && tar -xzf maldetect-current.tar.gz

Changing directories in scripts

Be careful with changing directories in scripts. If an error happens in the middle of the operation, the next operation may find itself in an unexpected directory (in the absence of adequate error handling).

Changing directories can become safer using (...) sub-shells. For example instead of this, with a cd ~ at the end:

cd /usr/local/src && wget http://www.rfxn.com/downloads/maldetect-current.tar.gz && tar -xzf maldetect-current.tar.gz
cd maldetect-* && bash ./install.sh && cd ~

You could do this, without the need for the cd ~ at the end:

    cd /usr/local/src && wget http://www.rfxn.com/downloads/maldetect-current.tar.gz && tar -xzf maldetect-current.tar.gz
    cd maldetect-* && bash ./install.sh

Function declaration style

This is the recommended writing style:

nginx() {
    # ...

That is, no function keyword, and () after the name.

I would also give this function a more descriptive name.


Instead of:

mkdir /root/backups /root/backups/db /root/backups/dirs

You could write more compactly as:

mkdir -p /root/backups/{db,dirs}

Instead of cd ~, you could write simply cd.

  • \$\begingroup\$ Thank you janos. Will you please try to edit only the first notification about set -euo pipefail? I am not sure where I should put it (took a glimpse in some code examples from a Google search but not sure how to use it), and I'm also not sure I totally understood how it works and where I should examine it's stdout or if I could view it fully in the terminal without doing some dedicated action (like redirecting it to a file), as I've already had cases I couldn't navigate up in the window because ti seem to have ended even if more stdout was before. So, please try to clarify a bit on this. \$\endgroup\$
    – user125391
    Aug 6, 2017 at 3:44
  • \$\begingroup\$ @Benia it's common to put it right after the shebang line (after #!/bin/bash). You can put it anywhere, it will affect all commands that follow it. \$\endgroup\$
    – janos
    Aug 6, 2017 at 6:24
  • \$\begingroup\$ By "after" you mean after in the same row, or one row under? If it comes after in the same line, shouldn't these be separated by space, hyphen and space (like #!/bin/bash -x)? \$\endgroup\$
    – user125391
    Aug 6, 2017 at 13:01
  • \$\begingroup\$ @Benia after the line. On the next line. \$\endgroup\$
    – janos
    Aug 6, 2017 at 13:05
  • \$\begingroup\$ Hello dear @janos. You might want to see this QA session here in CR, also regarding Nginx. \$\endgroup\$
    – user125391
    Oct 23, 2017 at 19:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.