setting up a wordpress/rails vps with ansible
February 19, 2017
For my latest project with Mountain Tech Media I decided to take some extra time use Ansible to set up my VPS. I looked at Ansible as well as Chef, and while I don't pretend to be an expert on either, I found Ansible to be a bit more friendly for beginners with small projects who just want to get up and running. I found this tutorial to be incredibly helpful in getting a Vagrant development environment setup. Check it out.
There were however a few small issues that caused me a disproportionate level of confusion in getting things setup:
how much memory?
It turns out that 256 Mb of memory isn't enough to install MySQL. You will likely get the error: invoke-rc.d: initscript mysql, action "start" failed.
dpkg: error processing package mysql-server-5.5 (--configure):
. To fix this, I increased memory allocation to 1Gb:
config.vm.define :node do |node|
# the droplet to provision
node.vm.box = "bento/ubuntu-16.04"
node.vm.hostname = "web1"
node.vm.network :private_network, ip: "10.0.15.11"
node.vm.network "forwarded_port", guest: 80, host: 8080
node.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
end
end
Which Kernel
You'll also notice in the above that I am using bento/ubuntu-16.04
as the base kernel. The screencast uses ubuntu/trusty64
but I wanted to use xenial. The problem here is that the ubuntu/xenial
kernel does not set up the assumed default user/password as vagrant
which throws a wrench into some things. Rather than customize it, I just switched to bento and everything works fine.
Configuring Nginx
note: part of the following section is a copy/paste of my own words originating from here
I am using passenger to serve my app, but letting nginx serve my precompiled assets. I spent a lot of time confused by how to make this work. Here is the config file I am using in /etc/nginx/sites-enabled/default
server {
listen 80 default_server;
listen [::]:80 default_server;
root {{wp['root_dir']}};
index index.php index.html index.htm index.nginx-debian.html;
passenger_enabled on;
passenger_app_env production;
server_name {{ ansible_eth0["ipv4"]["address"] }};
location / {
#try_files $uri $uri/ =404;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { log_not_found off; access_log off; allow all; }
location = .(css|gif|ico|jpeg|jpg|js|png)$" # don't include this line! see below!
location ~ ^/h20-initiative(/.\*|$) {
alias /home/{{user_name}}/unify/current/public$1;
passenger_base_uri /h20-initiative;
passenger_app_root /home/{{user_name}}/current/unify;
passenger_document_root /home/{{user_name}}/unify/current/public;
passenger_enabled on;
}
location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;
add_header ETag "";
break;
}
}
Here I am pointing requests for /
to my Wordpress install (the variable wp['root_dir'] determines this.) and requests to /h20-initiative/*
to to my rails app.
I also ran into a strange problem where static assets in my rails app were not being served. After a good deal of head scratching the Googling around, I was given some advice on how to grep through the nginx log. First I set my nginx log level to debug
. To do that, go to /etc/nginx/nginx.conf
. Find the line error_log /var/log/nginx/error.log;
and change it to error_log /var/log/nginx/error.log debug;
Then I greped through the log. I searched for one of the assets that wasn't being served and found these lines:
2017/03/01 02:02:31 [debug] 28274#28274: *231 test location: "/"
2017/03/01 02:02:31 [debug] 28274#28274: *231 test location: "robots.txt"
2017/03/01 02:02:31 [debug] 28274#28274: *231 test location: "favicon.ico"
2017/03/01 02:02:31 [debug] 28274#28274: *231 test location: ~ "\.php$"
2017/03/01 02:02:31 [debug] 28274#28274: *231 test location: ~ "/\.ht"
2017/03/01 02:02:31 [debug] 28274#28274: *231 test location: ~ "\.(css|gif|ico|jpeg|jpg|js|png)$"
2017/03/01 02:02:31 [debug] 28274#28274: *231 using configuration "\.(css|gif|ico|jpeg|jpg|js|png)$"
It looks like the block "\.(css|gif|ico|jpeg|jpg|js|png)$"
was the matched block and used to determine where to find requests for static assets. The problem is, that's not where those files are! In fact, I don't need to serve any assets from the root directory. I deleted that block, allowing for the intended block to serve my assets.
User installed plugins themes and updates.
I want my admin users to have permission to install plugins, themes and updates. We only have two admin users and I work closely with them to ensure nothing malicious is installed. We struggled a bit with this ability until I realized that permissions in the WP install were not done correctly. The owner of the process running Nginx needs permission to read and write to the directories. I did this by granting 0770 permission to all files and directories in the wordpress install.
Running rails app at sub path
The main site will be served from /
and the rails app from /h20-initiative. This presents a somewhat tricky problem in how to get urls to point to the right place. For example I have some Javascript that points to an end point in my app:
d3.json("/places/active", function(error, activePlaces){
// do something with the result
}
This works perfectly fine in development but in production the endpoint is at /h20-initiative/places/active
. How do we consolidate this? I could do all of my app development inside of a vagrant box that's running both my Wordpress install and this app. But that's an awful lot of overhead just to solve a name spacing issue when all I'm trying to do is hack on a rails project and ignore the rest. Instead, I just stored the apps' relative path in an environment variable and then prepend it in paths that need it. For example:
In my Javascript...
// in the view attach a variable to the window object:
:javascript
window.base_url = "#{ENV["BASE_URL"]}";
// then access it in the javascript
d3.json(base_url + "/places/active", function(error, activePlaces){
// do something with the result
}
For all of my route helpers I can add the relative path in config/application
Bundler.require(*Rails.groups)
module Unify
class Application < Rails::Application
# ...
config.relative_url_root = ENV["BASE_URL"]
end
end
In scss I can use an erb tag and image_url
to reference an image I want for a
background
$bg-img: "<%= image_url 'cumberlands.jpg' %>";
body{
background:url($bg-img);
// ...
}
And that's it! So much easier than having to change my development environment!