[[!meta title="Great Rails Hosting: A symlink for an app"]] ## Introduction As [[!ungleich]] focusses on educated customers, we meet pretty cool infrastructures from time to time. In some sense I count [[!localch]] as a customer: they supported me with one day off per week so I was able to found [[!ungleich]] and acquire first customers. This article is dedicated to [[!localch]] and describes a very elegant solution for Ruby on Rails hosting. ## Overview The setup consists of the following services, glued together in an elegant way: * [[!nginx]] * [[!unicorn]] * [[!bind]] * [[!capistrano]] * Symlinks ## Nginx The great trick of the setup is that nginx is used to forward requests to a unix socket that depends on the **hostname**, which is exposed as **$host** by nginx. The following configuration snippet contains the important parts: server { listen 80; location @error_page { root /var/nginx/$host/current/public; internal; [...] location ~ "^/assets/.*-[a-z0-9]{32}.\w+" { root /var/nginx/$host/current/public; [...] location ~ ^/assets/ { root /var/nginx/$host/current/public; [...] root /var/nginx/$host/current/public; location @unicorn { proxy_pass http://unix:/var/nginx/$host/unicorn.sock; # Forward original host name to be seen in unicorn proxy_set_header Host $host; # Server name and address like being available in PHP proxy_set_header SERVER_NAME $server_name; proxy_set_header SERVER_ADDR $server_addr; # The real client IP address - header has ben setup by Zeus proxy_set_header X-Real-IP $http_x_cluster_client_ip; # Needed second header for rails - See SYS-1587 proxy_set_header X_FORWARDED_FOR $http_x_cluster_client_ip; As you can see, all paths are dependent on the actual hostname as setup by nginx. ## Application Deployment Applications are deployed under their project name below **/var/nginx** (like **ws-locomotive.dev-deploy** or **ws-locomotive.master**). As you can see from the naming, developers can deploy one application from different branches easily (dev-deploy and master branches is this case). Developers can use [[!capistrano]] to deploy their applications and don't need to interact (reload/restart) with nginx, as it is already configured to accept any hostname. ## Name Server Configuration As you can imagine, it would be quite cumbersome for developers to reach a host named **ws-locomotive.dev-deploy**. That is why a wildcard domain is configured to point to the host running nginx: *.play.intra.local.ch. CNAME rails-dev-vm-snr01.intra.local.ch. ## Give the application a name A new hostname can be assigned to an application simply by symlinking it to the application: % cd /var/nginx % ln -s ws-locomotive.dev-deploy my-fancy-name.play.intra.local.ch This way, developers can use **any name** below play.intra.local.ch for their application. Some applications actually behave differently depending on the name they are accessed with: info.ws-locomotive.master.play.intra.local.ch -> ws-locomotive.master hp.ws-locomotive.master.play.intra.local.ch -> ws-locomotive.master ## Conclusions The setup is pretty elegant, because it allows developers to create new development environments without interacting with any sysadmin to configure nginx, bind or whatsoever. There is a security drawback though: An attacker could try to use hostnames like **../../../../etc/** and request the file **passwd**. That is the reasion why this service is not exposed to the outside world directly, but all external requests are filtered (whitelisting) by a load balancer in front of the rails hosts. [[!tag net unix foss ungleich localch rails]]