This post explains the NOLEG stack and provides a script that will get you running with 2 node sites, running securely on a linux server behind nginx while being a git server with hooks for deploying your site every time you commit to your private repository all within 10 minutes (some assumptions made)..
If you want to get started real fast, start your Ubuntu server and run setup.sh on it changing the DOMAIN setting to what ever domain you are using. You should be able to go to http://www.example.com and http://www.example.com/blog and see 2 different sites along with having your very own git server.
All you need to do is run the following commands on a fresh server (replace example.com with your domain (don't include the www)) and you are all set.
#!/bin/bash DOMAIN=example.com sudo apt-get install -y git git clone https://github.com/garrows/noleg-stack.git cd noleg-stack cat setup.sh | sed -e "s/example.com/$DOMAIN/g" > setup.sh chmod 777 setup.sh ./setup.sh
If this doesn't work for you or you would like to learn a few things, follow the step by step instructions below.
This post assumes that you:
Startup your Ubuntu Server. I'm using an M1.Micro on Amazon's EC2 running Ubuntu Server 12.04.3 LTS.
Make sure you open up port 80 and 22 on the security group or firewall. Also setup your DNS servers to point beta and www to the server's public IP address.
You can open up port 3000 and 2368 if you want to do some individual node site testing too.
Now SSH into your server.
Install nginx using the command
sudo apt-get update sudo apt-get install -y nginx sudo nginx
Test it works by going to www.example.com.
Having trouble? Try the official nginx docs.
Install git with
sudo apt-get install -y git git --version
If it works, you should get a version printed at the end.
Having trouble? Try the official git book.
Install mongo with
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list sudo apt-get update sudo apt-get install mongodb-10gen mongo --version
You should get mongoDB's version printed at the end if this worked. If not, try the MongoDB Install Guide.
Install nodejs using the following commands.
sudo apt-get install -y python-software-properties python g++ make sudo add-apt-repository -y ppa:chris-lea/node.js sudo apt-get update sudo apt-get install -y nodejs node -v
If it worked, the last line should print out the nodejs version.
Having trouble? Try the official nodejs docs.
We will need a few global nodejs packages later on so lets install them now.
sudo npm install -g forever express
Included in this repo are some config files. Lets get them now to use later on.
cd /tmp git clone https://github.com/garrows/noleg-stack.git
We will setup git to be accessed over ssh. Since you will probably be sharing access, we will create a special user that only has access to git commands and only accessible using approved keypairs instead of a password.
Lets setup the user first and add it to the www-data group which was setup by nginx.
sudo adduser --shell $(which git-shell) --gecos 'git version control' --disabled-password git sudo usermod -a -G www-data git sudo chsh -s /usr/bin/git-shell git sudo mkdir -p /home/git/.ssh sudo touch /home/git/.ssh/authorized_keys sudo chmod 600 /home/git/.ssh/authorized_keys sudo chmod 700 /home/git/.ssh sudo chown -R git:git /home/git/
You can setup your accepted public keys now by putting them in the authorized_keys file. If you are on ec2, you can use the generated key if you want.
cat /home/ubuntu/.ssh/authorized_keys | sudo tee -a /home/git/.ssh/authorized_keys
If you want to be able to commit to the git repo while being on the ec2 instance, you can generate a keypair on the server for the ubunut user to be trusted by the git user.
ssh-keygen -t rsa -N '' -f /home/ubuntu/.ssh/id_rsa cat /home/ubuntu/.ssh/id_rsa.pub | sudo tee -a /home/git/.ssh/authorized_keys
Now setup an empty git repository.
sudo mkdir -p /opt/git/website.git cd /opt/git/website.git sudo git --bare init sudo chown -R git:www-data /opt/git/website.git
If everything is setup correctly, you should be able to clone the empty repo remotely (ignore the empty repository warning).
cd /tmp/ git clone firstname.lastname@example.org:/opt/git/website.git
The global npm module express is able to generate a skelleton webapp by running the express command. We could just generate the app directly in the /opt/git/website.git directory but we will have to keep fixing permissions so lets generate this in a temp directory and commit it using the proper git workflow. You can of course do this on you computer instead.
cd /tmp/ git clone email@example.com:/opt/git/website.git cd website express --force --sessions --css stylus --ejs www echo "node_modules" > .gitignore git add . git commit -m "Initial commit" git push origin master
You will now have a blank web app commited to your repository. You can do a git pull on your local machine to get a copy. If you would like to give it a test out, you must run npm install first to download the packages.
There are more options to the express command that are documented on the official website
Lets also setup a Ghost blog separately so we can demonstrate virtual hosts (running multiple sites on the one server).
cd /tmp/website wget https://ghost.org/zip/ghost-0.4.0.zip sudo apt-get install unzip unzip ghost-0.4.0.zip -d blog rm ghost-0.4.0.zip git add . git commit -m "Added blog" git push origin master
You might want to update the Ghost link version from above to the latest.
By default Ghost blog's database is a local sqlite database. Lets move it out of its default directory so we don't destroy its contents when we deploy new code.
sudo mkdir -p /opt/ghostdb sudo cp -R /tmp/website/blog/content /opt/ghostdb sudo chown -R git:www-data /opt/ghostdb sudo chmod -R 770 /opt/ghostdb cp config.js config.js.orig cat config.js.orig | sed -e "s/__dirname/\'\/opt\/ghostdb\'/g" > config.js git add config.js config.js.orig git commit -m "Updated ghost config so the database is not lost" git push orgin master
Here are the gettting started docs in case you ran into troubles.
So now we have an express site and a ghost blog but they aren't running anywhere. Git hooks are located in the /opt/git/website.git/hooks/ directory and are basically scripts that will run automatically by git when you commit a change. We can use these to checkout the code to another directory to be run by a daemon.
First setup the folder permissions with this
sudo chgrp -R www-data /var/www sudo chmod -R g+w /var/www
Lets create the script /opt/git/website.git/hooks/post-commit from the sample in this repo.
sudo cp /tmp/noleg-stack/post-receive.sh /opt/git/website.git/hooks/post-receive sudo chmod 755 /opt/git/website.git/hooks/post-receive sudo chown git:www-data /opt/git/website.git/hooks/post-receive
Now lets test it by committing something.
cd /tmp/website touch README.md git add README.md git commit -m "Added readme to test auto publish" git push
If everything worked you should see a big log of npm installs and a symbolic link at /var/www/current pointing to the latest version of the code. The reason we create a fresh install each commit is if the install or script fails, the link isn't updated and the current code continues to run just fine.
You can test your website now by starting node.
sudo node /var/www/current/www/app.js sudo node /var/www/current/blog/index.js
The git hook will also look for the upstart processes node-www and node-blog and restart them if needed. We create them in the next step so it won't do anything right now.
We have our two node apps working now but we need a way to keep node running after reboots and unhandled exception. We will use the upstart.conf config file that starts the forever npm command as a upstart daemon. Forever will restart node when it crashes or exits unexpectedly and upstart will start the daemon after system reboots.
cat /tmp/noleg-stack/upstart.conf | sed -e "s/%APPLICATION%/node-www/g" | sed -e "s/%PATH%/\/var\/www\/current\/www\/app.js/g" > node-www.conf cat /tmp/noleg-stack/upstart.conf | sed -e "s/%APPLICATION%/node-blog/g" | sed -e "s/%PATH%/\/var\/www\/current\/blog\/index.js/g"> node-blog.conf chmod 777 node-www.conf chmod 777 node-blog.conf sudo mv node-*.conf /etc/init/
To help increase security, its a good idea to run the node process as its own user. Lets make one called 'nodeuser'.
sudo adduser --gecos 'node daemon user' --disabled-password nodeuser sudo usermod -a -G www-data nodeuser sudo usermod -a -G git nodeuser
We also need a directory setup for the logs to go
sudo mkdir -p /var/log/node sudo chown nodeuser:www-data /var/log/node
The git hook we setup earlier will try to restart the upstart jobs every time it updates the code. Ubuntu only allows privileged (root) users restart upstart jobs so we need to add a rule into the sudoers directory to tell ubuntu that the git user can do this. I would prefer not to give full rights here but I don't see any other way. This should be secure though since nobody should be able to modify the hook remotely.
echo "git ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/node-restart sudo chmod 440 /etc/sudoers.d/node-restart
Now lets start the services ourselves.
sudo start node-www sudo start node-blog
If everything went to plan you should have 2 sites running http://example.com:3000 and http://example.com:2368. If the blog (http://example.com:2368) doesn't work, don't worry, that is because of the host ip address in the config. Nginx will fix this for us.
Now that we have two separate node processes running on different ports, we need to tell nginx to route different hostnames and urls to the different ports. Here is the summary of the configs we will setup:
We will use nginx.conf as a base config file for nginx that we can substitute some values into for our purposes.
cat /tmp/noleg-stack/nginx.conf | sed -e "s/%APPLICATION%/example.com/g" | sed -e "s/%PORTWWW%/3000/g" | sed -e "s/%PORTBLOG%/2368/g" > example.com sudo mv example.com /etc/nginx/sites-available/example.com sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com sudo service nginx restart
Now, if that worked, you should be all set with your sites running on www.example.com and www.example.com/blog so congratulations.
Please if you have found any errors or have any suggestions, either submit an issue, make a pull request or simply email me at firstname.lastname@example.org. I hope this helped you out in some way.