Blog


It is a pain to follow my own tutorial over and over for each domain. So I automated it! This script is tested ONLY on the official Ubuntu 10.04 32 bit AMI in a tiny instance, following the install instructions . I have yet to test it anywhere else, but if it works for you, please leave a comment at the bottom with your configuration. Also, if any Bash gurus have tips for the script, your input would be very much appreciated.

This script creates a new user for the website and creates a home directory on the mounted EBS volume to host the site. Then it creates a new pool configuration, to give the site more secure execution. After that, it creates a new virtual host in nginx, to tell the server how to deal with the new site. It creates a MySQL user and database. Finally, it downloads WordPress, and installs it into the web directory, and configures almost everything. When you visit the domain, you will be asked for the site title, admin user (for the blog) and password, and you email. After that, your blog is running!!

If you want to get the latest version of this script, you can download it here. I will post the current (very rough) script here, for posterity’s sake. It has basically no error checking, and probably isn’t very portable. It also exclusively works with nginx, PHP5-fpm, and MySQL, though this should be easy to fix. This script also requires a few template files, which are included at the bottom, and at the Github site.

Note: You will need the file /root/mysql with the root password on the first line and nothing else. You should run “chmod 600 /root/mysql” to ensure no one can read the root password.

#!/bin/bash
VHOST_DIR='/etc/nginx/sites-available'
USER_DIR='/ebs/www'
USERNAME=''
USERPASS=''
DOMAIN=''
MSYQLPASS=''
USER_TRUNC=''
DOMAIN_TRUNC=''
DB_PASS=''

RET=''
function sanity_check {
 if [ "$(id -u)" != "0" ]; then
 echo "Script must be run as root."
 exit 1
 fi
 if [[ $1 != 2 ]]; then
 echo $1
 echo "Usage: nginx-vhost.sh username example.com"
 exit 4
 fi

 egrep "^$USERNAME:" /etc/passwd >/dev/null
 if [ $? -eq 0 ]; then
 echo "$USERNAME exists!"
 exit 3
 fi

}

function password {
 RET=$(cat /dev/urandom | tr -cd [:alnum:] | head -c ${1:-16})
}

function setup_user {
 useradd -m -d $USER_DIR/$USERNAME -U $USERNAME

 password
 PASSWORD=$RET
 echo "$USERNAME:$PASSWORD" | chpasswd

 mkdir -p $USER_DIR/$USERNAME/$DOMAIN/htdocs
 mkdir $USER_DIR/$USERNAME/$DOMAIN/logs
 touch $USER_DIR/$USERNAME/$DOMAIN/logs/access.log
 touch $USER_DIR/$USERNAME/$DOMAIN/logs/error.log
 chown -R $USERNAME $USER_DIR/$USERNAME
 chgrp -R $USERNAME $USER_DIR/$USERNAME

 echo "Create user: $USERNAME with password: $PASSWORD"
}
function php_pool {
 if [ ! -f /etc/php5/fpm/port ]; then
 echo "Cannot access /etc/php5/fpm/port"
 exit 2
 fi

 # Grab the port from file, and increment.
 PORT=$(cat /etc/php5/fpm/port)
 echo $(($PORT + 1)) > /etc/php5/fpm/port

 cp /etc/php5/fpm/pool.template /etc/php5/fpm/pools/$DOMAIN
 sed -i "s|example.com|$DOMAIN|" /etc/php5/fpm/pools/$DOMAIN

 sed -i "s|PORT|$PORT|" /etc/php5/fpm/pools/$DOMAIN
 sed -i "s|user = example|user = $USERNAME|" /etc/php5/fpm/pools/$DOMAIN
 sed -i "s|group = example|group = $USERNAME|" /etc/php5/fpm/pools/$DOMAIN
 echo "Added $DOMAIN to the PHP pool"
}
function nginx_vhost {
 cp /etc/nginx/vhost.template /etc/nginx/sites-available/$DOMAIN
 sed -i "s|example.com|$DOMAIN|g" /etc/nginx/sites-available/$DOMAIN
 sed -i "s|username|$USERNAME|g" /etc/nginx/sites-available/$DOMAIN
 sed -i "s|PORT|$PORT|g" /etc/nginx/sites-available/$DOMAIN
 # Should probably sed through and replace /ebs/www with $USER_DIR

 # Enable the site
 ln -s /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/$DOMAIN
 echo "Enabled $DOMAIN in the web server"
}
function server_reload {
 /etc/init.d/php5-fpm reload
 /etc/init.d/nginx reload
 echo "Servers reloaded"
}
function prepare_mysql {
 # Generate dbuser password
 password
 DB_PASS=$RET

 # Truncate username (15 chars max) and dbname (63 chars max)
 USER_TRUNC=$(echo $USERNAME | cut -c1-15)

 # This should be a separate user with only create perms.
 echo "CREATE DATABASE $USER_TRUNC;
GRANT ALL PRIVILEGES ON $USER_TRUNC.* to '$USER_TRUNC'@'localhost' IDENTIFIED BY '$DB_PASS';" > $USERNAME.sql
 mysql -u root -p$(cat /root/mysql) < $USERNAME.sql
 rm $USERNAME.sql
 echo "Created MySQL user: $USER_TRUNC password: $DB_PASS database: $DOMAIN_TRUNC"
}
function install_wordpress {
 wget http://wordpress.org/latest.tar.gz
 tar xf latest.tar.gz
 mv wordpress/* $USER_DIR/$USERNAME/$DOMAIN/htdocs/
 cp $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config-sample.php $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php
 sed -i "s|database_name_here|$USER_TRUNC|g" $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php
 sed -i "s|username_here|$USERNAME|g" $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php
 sed -i "s|password_here|$DB_PASS|g" $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php

 # Add in salts from the wordpress salt generator
 wget https://api.wordpress.org/secret-key/1.1/salt/
 sed -i "s/|/a/g" index.html
# cat index.html | while read line; do
 # sed -i "s|define('.*',.*'put your unique phrase here');|$line|" $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php
# sed 1d $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php
# done
 sed -i '/#@-/r index.html' $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php
 sed -i "/#@+/,/#@-/d" $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php
 rm index.html
 rm latest.tar.gz

 # Add in FTP stuff, even though that's not defined yet..

 # Own these new files
 chown -R $USERNAME $USER_DIR/$USERNAME/$DOMAIN/htdocs/*
 chgrp -R $USERNAME $USER_DIR/$USERNAME/$DOMAIN/htdocs/*
 # Make sure no one else can read this file
 chmod 700 $USER_DIR/$USERNAME/$DOMAIN/htdocs/wp-config.php

 #echo "Wordpress for $DOMAIN installed."
 #echo "Visit http://$DOMAIN/wp-admin/install.php to complete installation"
}

##############################################################################
# Start of program
USERNAME=$1
DOMAIN=$2

sanity_check $#
setup_user
php_pool
nginx_vhost
server_reload
prepare_mysql
install_wordpress
exit 0

Here is the PHP pool template. You can modify this to give all your sites a different default.

nano /etc/php5/fpm/pool.template
----

[example.com]
listen = 127.0.0.1:PORT # Increase this for each pool
user = example
group = example
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 2

You will also have to create a file that contains the next available port number for FastCGI so nginx can communicate with PHP5-FPM.

nano /etc/php5/fpm/port
----
9001

Lastly, we need an nginx vhost template. This is where you can do a ton of customization, according to the nginx documents. This template is great for running WordPress. I don’t know how well it would run other PHP projects, but it can certainly be customized to make them work.

nano /etc/nginx/vhost.template
----

server {
  listen 80;
  server_name example.com;
  access_log /ebs/www/username/example.com/logs/access.log;
  error_log /ebs/www/username/example.com/logs/error.log;
  root /ebs/www/username/example.com/htdocs;

  location / {
    index index.php index.html index.htm;
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:PORT; # Increase this for each pool
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME    /ebs/www/username/example.com/htdocs$fastcgi_script_name;  # same path as above
        fastcgi_param PATH_INFO               $fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
    }
    # Static files
    if (-f $request_filename) {
      expires 30d;
      break;
    }
    if (!-e $request_filename) {
      rewrite ^(.+)$ /index.php?q=$1 last;
    }
  }
}

That’s it! Again, if this works for you or if you have a problem, please leave a comment with your configuration! It will help me improve the script for everyone. That’s what open source is all about right?



About Josh Gachnang

Josh Gachnang is a small business consultant with 5 years of experience in developing IT systems. His specialties include moving IT infrastructure to the cloud, standard and mobile web development using Python and Django, and promoting with social media.  ,  ,

5 Comment(s)
  1. Pingback: BASH: How To Write Better Scripts | Server Cobra

  2. Chris Gundersen November 23, 2011 at 1:59 pm

    Won’t you also need a file containing your mysql root password located at /root/mysql (for the prepare_mysql function)?

    • Josh Gachnang November 27, 2011 at 1:11 pm

      Whoops! You are correct. I updated the link. I’ll add some file checking in the next release.

      • Chris Gundersen November 27, 2011 at 3:40 pm

        BTW, this is fantastic! I’ve been using Nginx at work managing a Rails app dev project and decided to try and revitalize some of my linux skills (I do mostly .Net dev on MSSQL). This script is *extremely* useful!

        • Josh Gachnang November 27, 2011 at 11:01 pm

          Awesome! I’m glad it has been helpful to you. I’ve spent the last few months using Nginx to manage large Django deployments, and this script has been the basis of a lot of it! If you’d like to modify or improve the script, feel free to fork it on GitHub! https://github.com/pcsforeducation/Sysadmin-scripts

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>