[[user_switching]] === User Switching (security feature) === Phusion Passenger supports automatic 'user switching': by default, it attempts to run applications as the "right" user, instead of running all applications as the same user. To better understand the problem, let us consider the situation with PHP. There is a problem that plagues most PHP web hosts, namely the fact that all PHP applications are run in the same user context as the web server. So for example, Joe's PHP application will be able to read Jane's PHP application's passwords. This is obviously undesirable on many servers. Phusion Passenger's 'user switching' feature solves this problem. Applications are run as the owner of their "startup file". For Ruby apps, the startup file is `config.ru` (Rack and Rails >= 3) or `config/environment.rb` (Rails 1 and 2). For Python apps, the startup file is `passenger_wsgi.py`. So suppose that `config.ru` is owned by user 'joe', then Phusion Passenger will spawn the corresponding application as 'joe' as well. The exact rules are a little bit more complicated, and they're explained further down in this section. ==== Requirements User switching is only enabled when all of the following conditions are met: - When not using <> (this is probably the case): * The ifdef::apache[] <> endif::[] ifdef::nginx[] <> endif::[] option must be enabled. * The web server's control process must have root privileges. This is the case on most installations. - When using <>: * The Flying Passenger daemon must be run with root privileges. ==== Effects When not using Flying Passenger, the following table illustrates the effect for different combinations of the requirements. |========================================================== ifdef::apache[] | | **PassengerUserSwitching on** | **PassengerUserSwitching off** endif::[] ifdef::nginx[] | | **passenger_user_switching on** | **passenger_user_switching off** endif::[] | **Web server has root privileges** | User switching enabled. | User switching disabled. Apps are run as ifdef::apache[] <> and <>. endif::[] ifdef::nginx[] <> and <>. endif::[] | **Web server has no root privileges** | User switching disabled. Apps are run as the web server's user. | User switching disabled. Apps are run as the web server's user. |========================================================== When using Flying Passenger, the effect is as follows: |========================================================== | **Daemon run with root privileges** | User switching enabled. | **Daemon run without root privileges** | User switching disabled. Apps are run as the daemon's user. |========================================================== When user switching is enabled, the following rules are followed to determine what user an application should be run as. The first matching rule is the rule that will be followed. 1. If ifdef::apache[] <> or <> endif::[] ifdef::nginx[] <> or <> endif::[] are set, then the application will be run as the specified user/group. Thus, these options are a good way to override user switching settings. 2. If the startup file is owned by root or an unknown user, then the application will run as the user specified by ifdef::apache[] <> and <>. endif::[] ifdef::nginx[] <> and <>. endif::[] 3. Otherwise, the application is run as the owner of the startup file. ==== Caveats & troubleshooting If your application regularly encounters permission errors or fails to find certain files, then this is an indication that your application is started as a user that you did not intent it to be run as. Other symptoms include: - The application fails to start because Bundler complains that it cannot find gems. This probably indicates that Bundler does not have read access to the directory that contains Bundler-installed gems. - The application fails to start and its error message mentions the path '/nonexistent'. This probably indicates that your application is started as the 'nobody' user. This is because on many systems, the 'nobody' user's home directory is '/nonexistent'. To check whether it is indeed the case that your application is started as a different user than you intended to, see <>. The most likely reason why your application is started as 'nobody' is probably because your startup file is owned by 'root', by 'nobody' or by an unknown user. To fix this, change the owner of the startup file to the owner that you want to run the application as. Whatever user your application runs as, it must have read access to the <>, and read/write access to the application's 'logs' directory. [[finding_out_app_user]] ==== Finding out what user an application is running as To find our what user an application is started as, first access its URL in your browser so that Phusion Passenger starts the application. For example: ---------------------------------- http://www.example.local/ ---------------------------------- The application will now either successfully start or fail to start. If it fails to start then you will see an error page that tells you what user the application was being started as. If you do not see the error page in the browser then set ifdef::apache[] <> endif::[] ifdef::nginx[] <> endif::[] on. If the application successfully started, then run `passenger-status` to find the process's PID: ------------------------------------------------ .---------- General information ----------- Max pool size : 6 Processes : 1 Requests in top-level queue : 0 .---------- Application groups ----------- /webapps/example.local#default: App root: /webapps/example.local Requests in queue: 0 * PID: 16915 Sessions: 0 Processed: 1 Uptime: 2s CPU: 0% Memory : 9M Last used: 2s ago ------------------------------------------------ In the above example we see that the PID is 16915. Next, use 'ps' to find out the user that it is running as: -------------------------------------------------- # ps -o pid,user,comm -p 16915 PID USER COMM 16915 phusion Passenger RackApp: /webapps/example.local -------------------------------------------------- As you can see, the application in this example is being run as user 'phusion'. [[reducing_memory_usage]] === Copy-on-write memory support (reducing memory consumption of Ruby applications) === Phusion Passenger automatically leverages operating system virtual memory copy-on-write features in order to reduce the memory usage of Ruby applications. Experience has shown that this reduces memory usage by 33% on average. For this mechanism to work, a Ruby interpreter with a copy-on-write friendly garbage collector is required. The following Ruby interpreters have copy-on-write friendly garbage collectors: - MRI Ruby >= 2.0. Versions prior to 2.0 did not have a copy-on-write friendly garbage collector. - http://www.rubyenterpriseedition.com/[Ruby Enterprise Edition], which was Phusion's branch of MRI Ruby 1.8 with a copy-on-write friendly garbage collector and other enhancement. It has reached End-Of-Life as of 2012, but remains available for legacy systems. [[capistrano]] === Capistrano recipe === Phusion Passenger can be combined with link:http://capify.org/[Capistrano]. The following Capistrano recipe demonstrates Phusion Passenger support. It assumes that you're using Git as version control system. -------------------------------------------------- set :application, "myapp" set :domain, "example.com" set :repository, "ssh://#{domain}/path-to-your-git-repo/#{application}.git" set :use_sudo, false set :deploy_to, "/path-to-your-web-app-directory/#{application}" set :scm, "git" role :app, domain role :web, domain role :db, domain, :primary => true namespace :deploy do task :start, :roles => :app do run "touch #{current_release}/tmp/restart.txt" end task :stop, :roles => :app do # Do nothing. end desc "Restart Application" task :restart, :roles => :app do run "touch #{current_release}/tmp/restart.txt" end end -------------------------------------------------- [[bundler_support]] === Bundler support === Phusion Passenger has automatic support for link:http://gembundler.com/git.html[Bundler]. The support consists of loading your application under the environment defined by your Gemfile. In other words, Phusion Passenger loads your application as if 'bundle exec' was used. The Bundler support works as follows: - If you have a '.bundle/environment.rb' in your application root, then Phusion Passenger will require that file before loading your application. - Otherwise, if you have a 'Gemfile', then Phusion Passenger will automatically call `Bundler.setup()` before loading your application. It's possible that your application also calls `Bundler.setup` during loading, e.g. in 'config.ru' or in 'config/boot.rb'. This is the case with Rails 3, and is also the case if you modified your 'config/boot.rb' according to the link:http://gembundler.com/rails23.html[Bundler Rails 2.3 instructions]. This leads to `Bundler.setup` being called twice, once before the application startup file is required and once during application startup. However this is harmless and doesn't have any negative effects. Phusion Passenger assumes that you're using Bundler >= 0.9.5. If you don't want Phusion Passenger to run its Bundler support code, e.g. because you need to use an older version of Bundler with an incompatible API or because you use a system other than Bundler, then you can override Phusion Passenger's Bundler support code by creating an empty file 'config/setup_load_paths.rb'. If this file exists then it will be required before loading the application startup file. In this file you can do whatever you need to setup Bundler or a similar system. [[add_passenger_to_gemfile]] ==== Does Phusion Passenger itself need to be added to the Gemfile? It is never necessary to add Phusion Passenger to the application's Gemfile. In case of Phusion Passenger Standalone, it is not necessary to execute the `passenger` command through `bundle exec`. The reason for this is because Phusion Passenger automatically loads the Gemfile environment. Most other Ruby application servers do not automatically load the Gemfile environment, which is why they must be added to the Gemfile and be executed with `bundle exec`. Even when your application uses any of the Phusion Passenger APIs, you still do not need to add Phusion Passenger to the Gemfile. The only thing you need to do is to put Phusion Passenger API calls inside `if` blocks that check whether Phusion Passenger is active, by checking whether the `PhusionPassenger` namespace is defined: [code,ruby] ------------------------------------ if defined?(PhusionPassenger) ... end ------------------------------------ === Installing multiple Ruby on Rails versions === Each Ruby on Rails applications that are going to be deployed may require a specific Ruby on Rails version. You can install a specific version with this command: ----------------------------- gem install rails -v X.X.X ----------------------------- where 'X.X.X' is the version number of Ruby on Rails. All of these versions will exist in parallel, and will not conflict with each other. Phusion Passenger will automatically make use of the correct version. === Making the application restart after each request === In some situations it might be desirable to restart the web application after each request, for example when developing a non-Rails application that doesn't support code reloading, or when developing a web framework. To achieve this, simply create the file 'tmp/always_restart.txt' in your application's root folder. Unlike 'restart.txt', Phusion Passenger does not check for this file's timestamp: Phusion Passenger will always restart the application, as long as 'always_restart.txt' exists. NOTE: If you're just developing a Rails application then you probably don't need this feature. If you set ifdef::apache['RailsEnv development'] ifdef::nginx['rails_env development'] in your web server configuration, then Rails will automatically reload your application code after each request. 'always_restart.txt' is mostly useful when you're using a web framework that doesn't support code reloading by itself, of when you're working on a web framework yourself. [[sub_uri_deployment_uri_fix]] === How to fix broken images/CSS/JavaScript URIs in sub-URI deployments Some people experience broken images and other broken static assets when they deploy their application to a sub-URI (i.e. 'http://mysite.com/railsapp/'). The reason for this usually is that you used a static URI for your image in the views. This means your 'img' source probably refers to something like '/images/foo.jpg'. The leading slash means that it's an absolute URI: you're telling the browser to always load 'http://mysite.com/images/foo.jpg' no matter what. The problem is that the image is actually at 'http://mysite.com/railsapp/images/foo.jpg'. There are two ways to fix this. The first way (not recommended) is to change your view templates to refer to 'images/foo.jpg'. This is a relative URI: note the lack of a leading slash). What this does is making the path relative to the current URI. The problem is that if you use restful URIs, then your images will probably break again when you add a level to the URI. For example, when you're at 'http://mysite.com/railsapp' the browser will look for 'http://mysite.com/railsapp/images/foo.jpg'. But when you're at 'http://mysite.com/railsapp/controller'. the browser will look for 'http://mysite.com/railsapp/controller/images/foo.jpg'. So relative URIs usually don't work well with layout templates. The second and highly recommended way is to always use Rails helper methods to output tags for static assets. These helper methods automatically take care of prepending the base URI that you've deployed the application to. For images there is `image_tag`, for JavaScript there is `javascript_include_tag` and for CSS there is `stylesheet_link_tag`. In the above example you would simply remove the '' HTML tag and replace it with inline Ruby like this: --------------------------------------- <%= image_tag("foo.jpg") %> --------------------------------------- This will generate the proper image tag to `$RAILS_ROOT/public/images/foo.jpg` so that your images will always work no matter what sub-URI you've deployed to. These helper methods are more valuable than you may think. For example they also append a timestamp to the URI to better facilitate HTTP caching. For more information, please refer to link:http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html[the Rails API docs]. === Out-of-Band Garbage Work and Out-of-Band Garbage Collection **Available since Phusion Passenger 4.0.0.** **At this time, this feature is only available on Ruby.** The Out-of-Band Work feature allows you to run arbitrary long-running tasks outside normal request cycles. This works by letting current requests to the process finish, then telling the process to perform the out-of-band work, then resuming passing requests to the process after said work is finished. A specific (and perhaps primary) use case of of Out-of-Band Work is *Out-of-Band Garbage Collection*. The garbage collector is run outside normal request cycles so that garbage collection runs inside normal request cycles can finish a lot faster. This can potentially save tens to hundreds of milliseconds of latency in requests. Because Out-of-Band Work is implemented at the Phusion Passenger inter-process request routing level, and not by, say, spawning a thread inside the application process, Out-of-Band Work has the following useful properties: - It works well even with tasks that can pause all threads. The MRI Ruby garbage collector is a stop-the-world mark-and-sweep garbage collector. - Phusion Passenger can spawn more processes as necessary, in order to prevent situations in which all application processes are busy performing out-of-band work. Phusion Passenger guarantees that there's at least one process that's ready to process requests. Applications can use Out-of-Band Work as follows: 1. Request out-of-band work by outputting the `X-Passenger-Request-OOB-Work` header during a request. It does not matter what the value is. At this time, it is not possible to request out-of-band work from outside requests. 2. You can actually perform out-of-band work when you receive a `:oob_work` Phusion Passenger event. Note that even though you can request out-of-band work, there's no guarantee that Phusion Passenger will send an `oob_work` event in a timely manner, if at all. It is also possible that Phusion Passenger sends an `oob_work` event without you ever having requested one. This latter could for example happen if the OOB work is administrator-initiated. Do not make any assumptions in your code. Here's an example which implements out-of-band garbage collection using the Out-of-Band framework. This example code doesn't do anything when the code is not being run in Phusion Passenger, thanks to the `if` block. [source, ruby] ------------------------------------------------- # Somewhere in a controller method: # Tell Phusion Passenger we want to perform OOB work. response.headers["X-Passenger-Request-OOB-Work"] = "true" # Somewhere during application initialization: if defined?(PhusionPassenger) PhusionPassenger.on_event(:oob_work) do # Phusion Passenger has told us that we're ready to perform OOB work. t0 = Time.now GC.start Rails.logger.info "Out-Of-Bound GC finished in #{Time.now - t0} sec" end end ------------------------------------------------- For your convenience, Phusion Passenger provides a Rack middleware for out-of-band garbage collection. Add the following to your `config.ru`. Likewise, this example code doesn't do anything when the code is not being run in Phusion Passenger, thanks to the `if` block. [source, ruby] ---------------------------------------------------- if defined?(PhusionPassenger) require 'phusion_passenger/rack/out_of_band_gc' # Trigger out-of-band GC every 5 requests. use PhusionPassenger::Rack::OutOfBandGc, 5 end ---------------------------------------------------- It should be noted that, although the application uses the Phusion Passenger API, it is <>. References: - link:http://blog.phusion.nl/2013/01/22/phusion-passenger-4-technology-preview-out-of-band-work/[The Phusion Blog article which first introduced this feature.] [[flying_passenger]] === Flying Passenger :version: 4.0.6 include::enterprise_only.txt[] Flying Passenger allows one to decouple Phusion Passenger's life time from the web server's life time, so that the web server can be independently restarted from Phusion Passenger, and from any of the application processes served by Phusion Passenger. Normally, Phusion Passenger starts together with the web server, and shuts down together with the web server. The advantages of this default behavior is that it makes Phusion Passenger easy to administer: one only has to deal with the web server process and can expect all relevant processes to be cleaned up after a web server shut down. However this also brings about a disadvantage: every time one restarts the web server (e.g. to make a minor configuration change), Phusion Passenger and all its application processes also get restarted. This problem is solved by 'Flying Passenger', which is an advanced mode of operation in Phusion Passenger that allows the web server to be indepedently restarted from Phusion Passenger. When this mode is enabled: - One must start Phusion Passenger separately from the web server, namely by starting the Flying Passenger daemon. This daemon must - to an extent - be separately configured and managed from the web server. - The web server must be configured to forward requests to the Flying Passenger daemon. - You should beware of the <>. ==== Requirements At this time, this feature is **only available in the Enterprise version of Phusion Passenger for Nginx**. You must have Phusion Passenger for Nginx properly installed. ==== Basic usage Start the Flying Passenger daemon by invoking the `flying-passenger` command. The only required option is `--socket-file`. Depending on whether you wish to enable <>, you have to start `flying-passenger` with root privileges or not. ------------------- $ sudo flying-passenger --socket-file=/var/run/flying-passenger.sock I, [2013-06-14T09:10:13.095339 #77179] INFO -- : Welcome to Flying Passenger 4.1.0 I, [2013-06-14T09:10:13.095339 #77179] INFO -- : Starting PassengerWatchdog... I, [2013-06-14T09:10:13.097036 #77179] INFO -- : PassengerWatchdog started on PID 77181 ... I, [2013-06-14T09:10:13.129017 #77179] INFO -- : PassengerWatchdog initialized properly I, [2013-06-14T09:10:13.129127 #77179] INFO -- : Flying Passenger up and listening on /var/run/flying-passenger.sock! ------------------- Now configure Phusion Passenger for Nginx to make use of the Flying Passenger daemon, by setting the `passenger_fly_with` option to the socket filename: ------------------- http { ... passenger_fly_with /var/run/flying-passenger.sock; ... } ------------------- After (re)starting Nginx, Nginx + Flying Passenger is fully operational: ------------------- $ sudo /path-to/nginx ------------------- You can test it by adding a virtual host for a web app: ------------------- http { ... server { listen 80; server_name www.foo.local; root /webapps/foo/public; passenger_enabled on; } } ------------------- Verify that it works by making an HTTP request to it: --------------------------------- $ curl http://www.foo.local/ --------------------------------- Now let's verify that restarting the web server does not restart the just-spawned application process. Run `passenger-status` to obtain the PID of the application process: --------------------------------- $ sudo passenger-status Version: 4.1.0 Date : 2013-06-14 09:21:51 -0400 .---------- General information ----------- Max pool size : 6 Processes : 1 Requests in top-level queue : 0 .---------- Application groups ----------- /webapps/foo#default: App root: /webapps/foo Requests in queue: 0 * PID: 77283 Sessions: 0 Processed: 1 Uptime: 2s CPU: 1% Memory : 8M Last used: 2s ago --------------------------------- As you can see, the PID of the application process is **77283**. Now let's see what happens if we restart Nginx: ------------------------ $ sudo /path-to/nginx -s stop $ sudo /path-to/nginx $ sudo passenger-status ------------------------ The application process should remain there, unchanged: --------------------------------- $ sudo passenger-status Version: 4.1.0 Date : 2013-06-14 09:21:51 -0400 .---------- General information ----------- Max pool size : 6 Processes : 1 Requests in top-level queue : 0 .---------- Application groups ----------- /webapps/foo#default: App root: /webapps/foo Requests in queue: 0 * PID: 77283 Sessions: 0 Processed: 1 Uptime: 18s CPU: 1% Memory : 8M Last used: 18s ago --------------------------------- [[configuring_flying_passenger]] ==== Configuring Flying Passenger Flying Passenger gets *some* configuration from the web server, but not all. In particular, most web server directives that are only valid in the `http` context, e.g. <>, have no effect when using Flying Passenger. Instead, you are supposed to pass these configuration directives through command line options to the Flying Passenger daemon. Configuration directives that have no effect on Flying Passenger are documented as such. You can assume that configuration directives that are not documented as such, work fine on Flying Passenger. For example, to achieve the same effect as setting passenger_log_level to 2, run the Flying Passenger daemon as follows: ---------------------------------- $ sudo flying-passenger --socket-file=/var/run/flying-passenger.sock --log-level=2 ---------------------------------- Currently, not all configuration directives have a Flying Passenger equivalent. Run the following command to see an overview of available options: ---------------------------- $ flying-passenger --help ---------------------------- ==== Managing the Flying Passenger daemon The Flying Passenger daemon runs in the foreground by default. This is undesirable on server environments. You can make it go into the background by passing `--daemonize`, `--log-file` and `--pid-file`: -------------------------------------------------------------------------------- $ sudo flying-passenger --socket-file=/var/run/flying-passenger.sock \ --daemonize --log-file=/var/log/flying-passenger.log \ --pid-file=/var/run/flying-passenger.pid -------------------------------------------------------------------------------- You can shut down a Flying Passenger daemon by sending SIGINT or SIGTERM to it: -------------------------------------------------------------------------------- $ kill `cat /var/run/flying-passenger.pid` -------------------------------------------------------------------------------- We recommend using link:http://cr.yp.to/daemontools.html[daemontools] or link:http://smarden.org/runit/[runit] for managing the Flying Passenger daemon. These tools will allow automatically starting the Flying Passenger daemon at boot, and will automatically restart the daemon if it crashes. You can create and enable a daemontools/runit service as folows: -------------------------------- $ sudo mkdir /etc/service/flying-passenger $ sudo nano /etc/service/flying-passenger/run #!/bin/sh exec /path-to/flying-passenger \ --socket-file=/var/run/flying-passenger.sock \ --log-file=/var/log/flying-passenger.log \ --pid-file=/var/run/flying-passenger.pid -------------------------------- Immediately after creating the `run` file, daemontools/runit automatically runs it to start the daemon. Note that the location (`/etc/service`) depends on the OS or Linux distros. Sometimes it's `/service`. Also note that we start the Flying Passenger daemon without `--daemonize`. To shut down a daemontools/runit-managed daemon, you need to use `svc -d /etc/service/flying-passenger` (daemontools) or `sv stop /etc/service/flying-passenger` (runit) instead of sending a signal to the process. [[flying_passenger_caveats]] ==== Caveats and limitations Beware of the following caveats and limitations when using Flying Passenger: - The Nginx executable **must** be compiled with the same version of Phusion Passenger as the Flying Passenger daemon. Failing to meet this requirement may result in cryptic errors, or may result in certain features not working, until you've fixed the situation. When upgrading Phusion Passenger, you must restart both Nginx and the Flying Passenger daemon. - The <> directive has no effect. When using Flying Passenger, you are not supposed to set `passenger_root`. - When you add a new application to the web server configuration, Flying Passenger will automatically pick up the application's settings and spawn this new application upon the first request to it. However it is not capable of automatically starting the new app before a request has been sent to it (i.e. <>-like behavior is not available in this case). As a workaround, you can send an HTTP request to your application after starting the daemon, which forces it to spawn application processes. - When you remove an application from the web server configuration, Flying Passenger will not detect the removal and will not shut down the associated application processes. Killing the application processes will also not help, because Flying Passenger will restart them per the (now-removed, but still in the Flying Passenger daemon's memory) <> settings. At the moment, there are two ways to get rid of those processes: * Before removing the application from the web server configuration, explicitly set its `passenger_min_instances` to 0. Next, send a request to it, which will cause the Flying Passenger daemon to take over the new `passenger_min_instances 0` option. You can then proceed with removing the application from the web server configuration, and restarting the web server. Finally, kill the PIDs associated to those application processes and remove the application configuration. * Restart the Flying Passenger daemon.