Why is my NGINX to pm2 upstream slow when restarting? - node.js

I run a home server with nginx reverse proxied to a Node.js/PM2 upstream. Normally it works perfectly. However, when I want to make changes, I run pm2 reload pname or pm2 restart pname, which results in nginx throwing 502 Bad Gateway for about 10-20 seconds before it finds the new upstream.
My Node.js app starts very fast and I am 99% sure it is not actually taking that long for the upstream to start and bind to the port (when I don't use the nginx layer it is accessible instantly). How can I eliminate the extra time it takes for nginx to figure things out?
From nginx/error.log:
2021/01/29 17:50:35 [error] 18462#0: *85 no live upstreams while connecting to upstream, client: [ip], server: hostname.com, request: "GET /path HTTP/1.1", upstream: "http://localhost/path", host: "www.hostname.com"
From my nginx domain config:
server {
listen 80;
server_name hostname.com www.hostname.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name hostname.com www.hostname.com;
# ...removed ssl stuff...
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
gzip_proxied no-cache no-store private expired auth;
gzip_min_length 1000;
location / {
proxy_pass http://localhost:3010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_read_timeout 240s;
}
}

This is caused by the default behavior for an upstream, this may not be obvious since you're not explicitly declaring your upstream using the upstream directive. Your configuration with an upstream directive would look like this:
upstream backend {
server localhost:3010;
}
...
server {
listen 443 ssl;
...
location / {
proxy_pass http://backend;
...
}
}
In this form it's apparent you're just relying on the default options for the server directive. The server directive has many options, but two of them are important here: max_fails and fail_timeout. These options control failure states and how nginx should handle them. By default max_fails=1 and fail_timeout=10 seconds, this means that after one unsuccessful attempt to communicate with the upstream nginx will wait 10 seconds before attempting again.
To avoid this in your environment you could simply disable this mechanism by setting max_fails=0:
upstream backend {
server localhost:3010 max_fails=0;
}

Related

Running nginx to serve files and act as a reverse proxy for Node app on same domain

I am currently trying to run Nginx as a reverse proxy for a small Node application and serve up files for the core of a site.
E.g.
/ Statically served files for root of website
/app/ Node app running on port 3000 with Nginx reverse proxy
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm;
# Set path for access_logs
access_log /var/log/nginx/access.example.com.log combined;
# Set path for error logs
error_log /var/log/nginx/error.example.com.log notice;
# If set to on, Nginx will issue log messages for every operation
# performed by the rewrite engine at the notice error level
# Default value off
rewrite_log on;
# Settings for main website
location / {
try_files $uri $uri/ =404;
}
# Settings for Node app service
location /app/ {
# Header settings for application behind proxy
proxy_set_header Host $host;
# proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Proxy pass settings
proxy_pass http://127.0.0.1:3000/;
# Proxy redirect settings
proxy_redirect off;
# HTTP version settings
proxy_http_version 1.1;
# Response buffering from proxied server default 1024m
proxy_max_temp_file_size 0;
# Proxy cache bypass define conditions under the response will not be taken from cache
proxy_cache_bypass $http_upgrade;
}
}
This appeared to work at first glance, but what I have found over time is that I am being served 502 errors constantly on the Node app route. This applies to both the app itself, as well as static assets included in the app.
I've tried using various different variations of the above config, but nothing I can find seems to fix the issue. I had read of issues with SELinux, but this is currently not on the server in question.
Few additional bits of information;
Server: Ubuntu 18.04.3
Nginx: nginx/1.17.5
2020/02/09 18:18:07 [error] 8611#8611: *44 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: x, server: example.com, request: "GET /app/assets/images/image.png HTTP/1.1", upstream: "http://127.0.0.1:3000/assets/images/image.png", host: "example.com", referrer: "http://example.com/overlay/"
2020/02/09 18:18:08 [error] 8611#8611: *46 connect() failed (111: Connection refused) while connecting to upstream, client: x, server: example.com, request: "GET /app/ HTTP/1.1", upstream: "http://127.0.0.1:3000/", host: "example.com"
Has anyone encountered similar issues, or knows what it is that I've done wrong?
Thanks in advance!
It's may be because of your node router.It's better to share nodes code too.
Anyway try put your main router and static route like app.use('/app', mainRouter); and see it make any sense?

Simply nginx reverse proxy from localhost:80 to localhost:8080 not working

im learning reverse proxy w/ nginx for the first time, and the following isnt working for me
im trying to reroute requests from http://localhost to an api server i have running at http://localhost:8080
server {
listen 80;
location / {
proxy_pass http://localhost:8080;
}
}
when i hit http://localhost, I simply get shown the welcome to nginx splash screen.
if i hit http://localhost:8080, i see my api
I have a node express service running at :8080, which i can hit manually, but shouldn't http://localhost be proxied there too?
When I setup a nginx domain that forwards requests to a node server, it looks like this, for the server_name, you can use localhost as a parameter for accessing it via localhost. You can also pass default_server to make this the default server config.
Note: Only one active config can contain default_server otherwise Nginx will throw errors.
Note: When using default_server, Nginx will catch localhost in that server config. Otherwise you need to specify localhost in the list of server_name's (separated by a space).
server {
# Setup the domain name(s)
server_name example.com;
listen 80 default_server;
# If you would like to gzip your stuff
gzip on;
gzip_min_length 1;
gzip_types *;
# Setup the proxy
# This will forward all requests to the server
# and then it will relay the servers response back to the client
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
}
Found out that adding this to my nginx.conf fixes the issue:
listen [::]:80;
For some reason listen 80; doesn't catch my http://localhost requests.

nginx responses 504/502 gateway timeout error at times

I faced with a problem.
I have 6 express apps running on Node and use Nginx as a reverse proxy, all of them running without a problem for months. But recently, when I'm trying to navigate to the inner page of any of site it returned me 502 or 504 nginx error.
When I try to run apps on ngrok or locally they work correctly but on the production server, I got 504/502 error.
Nginx log says
2019/04/10 16:38:12 [error] 1362#1362: *245 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 37.9.113.120, server: my.server, request: "GET /videos/videoId HTTP/1.1", upstream: "http://127.0.0.1:3000/videos/videoId", host: "www.my.host"
I tried to increment timeouts
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
But it didn't help (
Here is my server configuration.
server {
listen x.x.x.x:443 http2;
ssl on;
server_name www.myservername.com;
...(ssl conf here)
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I dig on StackOverflow similar topics but didn't find a solution. The most strange thing, in this case, is, after some time inner page is available but after I do loading tests and send about 100 requests on production server it stops working for about half or hour
Thanks for the help in advance.
You're listening on 443, using ssl, you must specify your certificates/key :
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /var/lib/nginx/ssl/serverssl.crt;
ssl_certificate_key /var/lib/nginx/ssl/serverssl.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_set_header Host example.com;
proxy_pass http://localhost:8192/;
}
}

When using Nginx as a reversed proxy, do I need to do routing for pure websocket application in expressjs?

Hard to write a proper title.
I have a pure websocket application which is based on an express server. In development mode the express server does some simple routing to access the html, js and png files. Basically it is a one page app that only handle websocket protocol. But in production mode, I delegate all that to Nginx so I don't do any routing in express production. I was expecting Nginx will find all these files on its "root" directory. I get a "Cannot Get /" error however.
TL;DR version:
When I use Nginx to serve static contents for a one page app, do I need to do anything in express routing? If so, how?
UPD (my Nginx settings, can find another more detail question here):
upstream upstream_project {
server 127.0.0.1:8888;
keepalive 64;
}
server {
listen 0.0.0.0:80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/local/share/html;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name ws_server;
location / {
try_files $uri $uri/ index.html;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://upstream_project;
proxy_read_timeout 240s;
}
}
UPDATED:
I have found in Nginx log that the client is trying to access the server via ipv6. See this:
kevent() reported that connect() failed (61: Connection refused) while
connecting to upstream, client: ::1, server: ws_server, request: "GET /
HTTP/1.1", upstream: "http://127.0.0.1:8888/", host: "localhost"
I remove the ipv6 server listen line (listen [::]:80 default_server ipv6only=on;) and have to change the try_files line to use file name explicitly.
try_files $uri $uri/app.js ;
Then I can get my app working. But I don't understand. Why do I have to do this?
I still can't access the static png file from the subdirectory of "root" folder. Any help would be appreciated.
NGINX supports WebSocket by allowing a tunnel to be set up between a client and a backend server. For NGINX to send the Upgrade request from the client to the backend server, the Upgrade and Connection headers must be set explicitly, as in this example:
location /wsapp/ {
proxy_pass http://wsbackend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Take a look on this NGINX guide for more details and examples.

Nginx upstream prematurely closed connection while reading response header from upstream, for large requests

I am using nginx and node server to serve update requests. I get a gateway timeout when I request an update on large data. I saw this error from the nginx error logs :
2016/04/07 00:46:04 [error] 28599#0: *1 upstream prematurely closed
connection while reading response header from upstream, client:
10.0.2.77, server: gis.oneconcern.com, request: "GET /update_mbtiles/atlas19891018000415 HTTP/1.1", upstream:
"http://127.0.0.1:7777/update_mbtiles/atlas19891018000415", host:
"gis.oneconcern.com"
I googled for the error and tried everything I could, but I still get the error.
My nginx conf has these proxy settings:
##
# Proxy settings
##
proxy_connect_timeout 1000;
proxy_send_timeout 1000;
proxy_read_timeout 1000;
send_timeout 1000;
This is how my server is configured
server {
listen 80;
server_name gis.oneconcern.com;
access_log /home/ubuntu/Tilelive-Server/logs/nginx_access.log;
error_log /home/ubuntu/Tilelive-Server/logs/nginx_error.log;
large_client_header_buffers 8 32k;
location / {
proxy_pass http://127.0.0.1:7777;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
}
location /faults {
proxy_pass http://127.0.0.1:8888;
proxy_http_version 1.1;
proxy_buffers 8 64k;
proxy_buffer_size 128k;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I am using a nodejs backend to serve the requests on an aws server. The gateway error shows up only when the update takes a long time (about 3-4 minutes). I do not get any error for smaller updates. Any help will be highly appreciated.
Node js code :
app.get("/update_mbtiles/:earthquake", function(req, res){
var earthquake = req.params.earthquake
var command = spawn(__dirname + '/update_mbtiles.sh', [ earthquake, pg_details ]);
//var output = [];
command.stdout.on('data', function(chunk) {
// logger.info(chunk.toString());
// output.push(chunk.toString());
});
command.stderr.on('data', function(chunk) {
// logger.error(chunk.toString());
// output.push(chunk.toString());
});
command.on('close', function(code) {
if (code === 0) {
logger.info("updating mbtiles successful for " + earthquake);
tilelive_reload_and_switch_source(earthquake);
res.send("Completed updating!");
}
else {
logger.error("Error occured while updating " + earthquake);
res.status(500);
res.send("Error occured while updating " + earthquake);
}
});
});
function tilelive_reload_and_switch_source(earthquake_unique_id) {
tilelive.load('mbtiles:///'+__dirname+'/mbtiles/tipp_out_'+ earthquake_unique_id + '.mbtiles', function(err, source) {
if (err) {
logger.error(err.message);
throw err;
}
sources.set(earthquake_unique_id, source);
logger.info('Updated source! New tiles!');
});
}
Thank you.
I solved this by setting a higher timeout value for the proxy:
location / {
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_pass http://localhost:3000;
}
Documentation: https://nginx.org/en/docs/http/ngx_http_proxy_module.html
I think that error from Nginx is indicating that the connection was closed by your nodejs server (i.e., "upstream"). How is nodejs configured?
I had the same error for quite a while, and here what fixed it for me.
I simply declared in service that i use what follows:
Description= Your node service description
After=network.target
[Service]
Type=forking
PIDFile=/tmp/node_pid_name.pid
Restart=on-failure
KillSignal=SIGQUIT
WorkingDirectory=/path/to/node/app/root/directory
ExecStart=/path/to/node /path/to/server.js
[Install]
WantedBy=multi-user.target
What should catch your attention here is "After=network.target".
I spent days and days looking for fixes on nginx side, while the problem was just that.
To be sure, stop running the node service you have, launch the ExecStart command directly and try to reproduce the bug. If it doesn't pop, it just means that your service has a problem. At least this is how i found my answer.
For everybody else, good luck!
I stumbled upon *145660 upstream prematurely closed connection while reading upstream Nginx error log entry when trying to download a 2GB file from the server Nginx was a proxy for. The message indicates that the "upstream" closed connection, but in fact it was related to proxy_max_temp_file_size setting:
Syntax: proxy_max_temp_file_size size;
Default: proxy_max_temp_file_size 1024m;
Context: http, server, location
When buffering of responses from the proxied server is enabled, and the whole response does not fit into the buffers set by the proxy_buffer_size and proxy_buffers directives, a part of the response can be saved to a temporary file. This directive sets the maximum size of the temporary file. The size of data written to the temporary file at a time is set by the proxy_temp_file_write_size directive.
The zero value disables buffering of responses to temporary files.
This restriction does not apply to responses that will be cached or stored on disk.
The symptoms:
download was being forcibly stopped at around 1GB,
Nginx claimed that upstream closed connection, but without proxy server was returning the full content.
The solution:
increased proxy_max_temp_file_size for proxied location to 4096m and it started sending full content.
I was finding this error in the logs of my AWS Elastic Beanstalk instance when trying to post about half a million rows to my api.
I followed all the advice here to no avail.
What did finally work was increasing the size of my EC2 instance from 1 core and 1GB RAM to 4 core and 8 GB RAM.
You can increase the timeout in node like so.
app.post('/slow/request', function(req, res) {
req.connection.setTimeout(100000); //100 seconds
...
}
I don't think this is your case, but I'll post it if it helps anyone. I had the same issue and the problem was that Node didn't respond at all (I had a condition that when failed didn't do anything - so no response) - So if increasing all your timeouts didn't solve it, make sure all scenarios get a response.
I ran into this issue as well and found this post. Ultimately none of these answers solved my problem, instead I had to put in a rewrite rule to strip out the location /rt as the backend my developers made was not expecting any additional paths:
┌─(william#wkstn18)──(Thu, 05 Nov 20)─┐
└─(~)──(16:13)─>wscat -c ws://WebsocketServerHostname/rt
error: Unexpected server response: 502
Testing with wscat repeatedly gave a 502 response. Nginx error logs provided the same upstream error as above, but notice the upstream string shows the GET Request is attempting to access localhost:12775/rt and not localhost:12775:
2020/11/05 22:13:32 [error] 10175#10175: *7 upstream prematurely closed
connection while reading response header from upstream, client: WANIP,
server: WebsocketServerHostname, request: "GET /rt/socket.io/?transport=websocket
HTTP/1.1", upstream: "http://127.0.0.1:12775/rt/socket.io/?transport=websocket",
host: "WebsocketServerHostname"
Since the devs had not coded their websocket (listening on 12775) to expect /rt/socket.io but instead just /socket.io/ (NOTE: /socket.io/ appears to just be a way to specify websocket transport discussed here). Because of this, rather than ask them to rewrite their socket code I just put in a rewrite rule to translate WebsocketServerHostname/rt to WebsocketServerHostname:12775 as below:
upstream websocket-rt {
ip_hash;
server 127.0.0.1:12775;
}
server {
listen 80;
server_name WebsocketServerHostname;
location /rt {
proxy_http_version 1.1;
#rewrite /rt/ out of all requests and proxy_pass to 12775
rewrite /rt/(.*) /$1 break;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://websocket-rt;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
I meet the same problem and no one of the solutions detailed here worked for me ...
First of all I had an error 413 Entity too large so I updated my nginx.conf as following :
http {
# Increase request size
client_max_body_size 10m;
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
##
# Proxy settings
##
proxy_connect_timeout 1000;
proxy_send_timeout 1000;
proxy_read_timeout 1000;
send_timeout 1000;
}
So I only updated the http part, and now I meet the error 502 Bad Gateway and when I display /var/log/nginx/error.log I got the famous "upstream prematurely closed connection while reading response header from upstream"
What is really mysterious for me is that the request works when I run it with virtualenv on my server and send the request to the : IP:8000/nameOfTheRequest
Thanks for reading
I got the same error, here is how I resolved it:
Downloaded logs from AWS.
Reviewed Nginx logs, no additional details as above.
Reviewed node.js logs, AccessDenied AWS SDK permissions error.
Checked the S3 bucket that AWS was trying to read from.
Added additional bucket with read permission to correct server role.
Even though I was processing large files there were no other errors or settings I had to change once I corrected the missing S3 access.
Problem
The upstream server is timing out and I don't what is happening.
Where to Look first before increasing read or write timeout if your server is connecting to a database
Server is connecting to a database and that connection is working just fine and within sane response time, and its not the one causing this delay in server response time.
make sure that connection state is not causing a cascading failure on your upstream
Then you can move to look at the read and write timeout configurations of the server and proxy.
This error can also occur when your code is getting into a loop. So investigate if you have any (indirectly) self-referencing code that's causing this.

Resources