Skip to main content

App Daemon

Our server uses NGINX as Reverse Proxy for HTTP and HTTPS traffic and it is used to serve static files and proxy requests to your app.

Because it operates for all users on the server, only a limited set of features are configurable, the only way to configure it is via the deployment system. Fornatunely, we also give you a special page to make customizing it much easier.

If you never heard NGINX before, you can read some of good resorces here:

You can either edit NGINX config directly or via our interactive form.

Editing NGINX config directly

The text area contains current NGINX script and you can freely edit it. When you click Apply it will convert your changes to deployment script like this:

nginx: |
server {
....
}

Behind the scene it will convert your input to YAML script represented in our NGINX deployment script reference hence you won't be able to change certain protected values such as IP addresses, PHP proxy pass, etc.

info

You won't be able to edit root path via direct script. To change it, use YAML config.

nginx:
root: public_html/public

Editing the root path this way will keep the rest config untouched.

Editing NGINX config interactively

The interactive NGINX config contains easy-to-read NGINX config that covers about 80% of usage for popular frameworks. When you click Apply it will convert your input to YAML script represented in our NGINX deployment script reference. The current setup shown represents auto-detected config of your choosing.

Beware! Changing NGINX config this way may erases your customized NGINX config not represented using the form.

OptionWhat is it?
Root PathThe directory where NGINX searches for static files before being forwarded to app
Resolve TypeWhat is the kind of your app?
- StaticNo app, just HTML files
- PHP-FPMPHP Handled by fastcgi_pass
- Phusion AppSpecial handling for Node.js/Python/Ruby apps
- Generic AppAny kind of app (Traffic forwarded via $PORT arg/envar)
- Forward PortForward to hardcoded/docker/unit port via proxy_pass
- Deny RequestsDeny all requests (useful for subfolder config)

The PHP-FPM mode has some useful preset

OptionWhat is it?
- SmartXAMPP-like webpages (hide .php extension)
- Root Index - SafeRoute all extension-less pages to /index.php
- Root Index - StandardRoute all pages to /index.php as REQUEST_URI
- Root Index - AlwaysRoute all pages to /index.php as PATH_INFO

Please refer to PHP deployment setup for each of security implication.

The Static mode also has some useful preset below and a way to configure custom 404 page.

OptionWhat is it?
- DefaultNothing special
- SmartAllow hiding .html extension
- Root IndexRoute all pages to /index.html (Treats all as 200 OK)
- Auto IndexAllow listing directory and expose all file lists

Editing NGINX Config via YAML Script

Below is the example of the configuration:

nginx:
fastcgi: 'on'
root: public_html
index: index.html index.php
ssl: 'on'

Please see NGINX deployment script reference for advanced configuration that we can offer.

Configure NGINX for HTTPS

The ssl option has three options: always, on, off. The always option will force the server to redirect all HTTP traffic to HTTPS. The default value for ssl is on.

Configure NGINX for PHP

The fastcgi option has three options: always, on, off. The default value for fastcgi is off.

This section is covered more in the PHP Deployment section.

Configure NGINX for Generic Apps

NGINX by default only forwards connection and can't invoke the desired app to run. We use Phusion Passenger so we can tell NGINX to invoke the app and forwards HTTP requests.

The minimum configuration to enable NGINX for Generic apps is:

nginx:
root: public_html/public
passenger:
enabled: on
app_start_command: npm start

Which is equivalent to running npm start in the public_html (parent of root) directory, with the PORT environment variable set to the port that NGINX is listening to.

Passenger's enabled: on config is required to indicate that Passenger's module is activated for given host.

It is important that root is set to the directory that contains the public directory. This is because NGINX will serve the public directory as the root directory for serving static files as you don't want to expose the entire app scripts to the public.

If the app must be called from a subdirectory, you can use the app_root option to specify the directory that contains the app. For example, if the main app file is in public_html/server/app.js, you can set the app_root to public_html/server:

nginx:
root: public_html/public
passenger:
enabled: on
app_root: public_html/server
app_start_command: node app.js

PORT environment variable

The PORT environment variable is set by Phusion Passenger. It is a random port that's chosen as a HTTP proxy between your app and the internet.

Your app must listen to PORT to be able receiving HTTP requests from NGINX.

You can tell the app to listen to this port with another envar name using env like this:

nginx:
passenger:
app_start_command: env HTTP_PORT=$PORT node app.js

Or you can set the port using the app's argument if available:

nginx:
passenger:
app_start_command: node app.js --port $PORT

If you have designed your app to listen to PORT envar, leave it all alone:

nginx:
passenger:
app_start_command: node app.js
warning

All examples here assume your app listen to 127.0.0.1, not localhost or ::1.

For example in rust, you might have to specify it like env TARGET=127.0.0.1:$PORT cargo run.

info

If you make a social media app bot, you'll realize that the only way to make it work is via a webhook. It's a limitation by design, as we're not allowing any software running 24/7 and running a background polling.

Read it more for: Telegram, Discord, WhatsApp.

Set Environment Variables

Passenger has three ways to set environment variables:

app_env option

The app_env option is special option that's used to set environment mode. The default value is production. This option sets the value to the following environment variables:

  • RAILS_ENV
  • RACK_ENV
  • WSGI_ENV
  • NODE_ENV
  • PASSENGER_APP_ENV

You can set this value other values such as development or test.

env_var_list option

The env_var_list option is used to set environment variables directly from NGINX. It's a list of environment variables to set. The value is a string that's in the format of KEY=VALUE. For example:

nginx:
passenger:
enabled: on
app_start_command: node app.js
env_var_list:
- APP_SECRET=SECRET_VALUE
- APP_CONFIG={"key":"value"}

~/.bashrc file

Passenger reads the ~/.bashrc file to set environment variables. You can set environment variables in this way. For example run this command to edit the ~/.bashrc file via SSH:

nano ~/.bashrc

Then add the following line to the file:

export APP_SECRET=SECRET_VALUE

The environment variable will be set when the app is started next time.

More information about setting environment variables can be found in the Passenger documentation.

Restarting App

Any edits either in the app file or ~/.bashrc file will not be applied until the app is restarted. You can restart the app forcibly by running restart in SSH, which will actually calls:

killall -u $USER

The alternative way to restart app can be found in the Passenger documentation. Global NGINX Reload will also restart your app unless you're using Proxfix NOHUP.

For some connection using Node CJS, Python WSGI or Ruby config.ru, you may need to restart by passenger-config restart-app ~. We have some reports restarting don't work with restart alone.

Websocket

Websocket should works out of the box since only one app is spawned per website configuration. However, The websocket disconnects if NGINX reloads. To retain websocket data after NGINX reload, use Proxfix NOHUP

Proxfix NOHUP

proxfix NOHUP mode is a custom made proxy app designed to overcome Phusion limits during NGINX restarts. To setup proxfix NOHUP mode, prefix your app start command with proxfix with NOHUP=1 envar like below:

nginx:
passenger:
enabled: on
app_start_command: proxfix node app.js
env_var_list:
- NOHUP=1

proxfix will call node app.js outside from NGINX tree. So, when NGINX restarts, your app will not killed, it retains it's memory. NGINX simply calls proxfix who then proxies NGINX requests to your already-running app.

This has a downside that when you change proxfix parameters, it will not be restarted. you must call restart to really restart it.

X-Forwarded-For and X-Forwarded-Proto

Passenger will always add X-Forwarded-For and X-Forwarded-Proto to give the app client IP addresses and HTTP scheme.

To determine client IP address or HTTP scheme reliably, you have to know how many layers of proxy needed to reach your site.

For example, this is the example of response headers from user IP 123.45.67.89 when there's no proxy via HTTPS.

x-forwarded-for: 123.45.67.89
x-forwarded-proto: https

If the user is same but you're under a webproxy such as CloudFlare (assume IP 32:231::1), then it would be like this

x-forwarded-for: 123.45.67.89, 32:231::1
x-forwarded-proto: https, http

The X-Forwarded-Proto is equally important to determine whether you should send a secure or plain cookies. This mean your app must be configured to trust proxy headers. Some framework may already do it, some other need be set explicitly.

Language-Specific Configuration

More information about configuring NGINX for specific languages can be found in the Deployment section.

Advanced Configuration

All other available options are specified in Deployment System section.

Global Configuration

Global configuration of NGINX cannot be changed. However, some of the configuration are set as optimally as possible. You can see more about the global configuration in this file. Some of the interesting configuration are:

  • Static files are cached with 7d expiration time.
  • Gzip are enabled for css, js, svg static files.
  • Maximum file upload size is set to 512MB.
  • There's rate limit of 2 requests per second with 500 burst request per IP address to mitigate DoS attack.
  • TLSv1.2 is the minimum HTTPS version to support, which works for browsers as old as Windows 7 era (with IE 11), older browsers such as in Windows XP era will not be able to open your website.