Ghost Nginx 502 Bad Gateway but npm start --production Works

February 5, 2017 1k views
Ghost Node.js Nginx

I have a weird situation where I am seeing npm start --production start up my Ghost blog within my droplet and my website appears, but when I close that port and run service ghost restart and service nginx restart I get a 502 Bad Gateway. I'm a bit confused because I thought service ghost start or service ghost restart runs the npm start --production command and keeps it running untilstopis called. However this does not seem to be the case. Can anyone point me in the right direction for the logs I should check that might indicate why the command line command run works, butservice` command does not?

Successful command:
npm start --production

> ghost@0.7.9 start /var/www/ghost
> node index
Migrations: Up-to-date at version 004
Ghost is running in production... 
Your blog is now available on http://www.connordphillips.com 

Service start

sudo service ghost start
start: Job is already running: ghost

My only question I have is around the chown command I ran and if this blocked permissions to the root user who is running the commands. I ran chown -R ghost:ghost ghost/*, but I don't have a ghost user, only a root user which is signed in. Could this mess with anything?

3 Answers
jtittle1 February 5, 2017
Accepted Answer

@connordphillips

Ok, so your Ghost configuration is fine. I tested the exact same on a fresh droplet and it works. The only thing you need to change in the NGINX server block is:

proxy_pass http://localhost:2368;

Change localhost to 127.0.0.1 to match the server defined in Ghosts' config.js file.

You'd then restart NGINX: service nginx restart

Once NGINX is restarted, you may have to do a hard refresh in-browser, or flush your browser cache. Sounds a bit odd, but I had to do this when I was swapping configuration during testing.

As a reference, this is what I used to setup my test server before using your configuration:

apt-get update \
&& apt-get upgrade -y \
&& apt-get -y install build-essential unzip zip \
&& cd /usr/local/src \
&& curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - \
&& sudo apt-get -y install nodejs \
&& curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip \
&& mkdir -p /var/www/ghost \
&& unzip -uo ghost.zip -d /var/www/ghost \
&& cd /var/www/ghost \
&& npm install --production \
&& sudo add-apt-repository -y ppa:nginx/stable \
&& sudo apt-get update \
&& sudo curl https://raw.githubusercontent.com/TryGhost/Ghost-Config/master/init.d/ghost -o /etc/init.d/ghost \
&& sudo useradd -r ghost -U \
&& sudo chown -R ghost:ghost /var/www/ghost \
&& sudo chmod 755 /etc/init.d/ghost \
&& sudo update-rc.d ghost defaults \
&& sudo update-rc.d ghost enable

The above uses NodeSource's NodeJS repositories for 6.x to install NodeJS/NPM, uses NGINX's repos to install NGINX, downloads and installs Ghost, creates the Ghost init script, and runs the initial setup for Ghost.

From there, I changed this block to use my own domain.

        url: 'http://www.test-site.com',
        mail: {},
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            },
            debug: false
        },

        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        },

and then used your NGINX server block (inserting my domain in place of the filler you have) and then ran:

service ghost restart \
&& service nginx restart
  • @jtittle I really appreciate all of the help you have provided. I finally fixed the issue and it was related to the upstart script that I posted above. I removed the old script and created a new ghost.config in /etc/init with the following config. I have a good feeling that the issue was due to the script using the path of the old nodejs rather than the nvm nodejs version (v4.7.3) that I just upgraded to for the newer version of ghost. This fixed the problem

    ghost.conf

    # ghost
    
    start on startup
    
    script
        cd /var/www/ghost
        exec /root/.nvm/v4.7.3/bin/npm start --production
    end script
    

@connordphillips

The root user can read from, write to, and execute anything, regardless of whether root owns the directory or file.

The default log directory is /var/log.

Within the above directory, there should be an nginx directory containing an error.log file and which you can then tail to get the last, i.e.

tail -20 /var/log/nginx/error.log

If you could also post your NGINX server block for your domain, we can take a look and see if there's anything out of place.

  • @jtittle here is a postbin with the last few lines http://pastebin.com/icNbDdsw. I'm also curious about my my init.d/ghost setup. I upgraded my node for the new ghost version using nvm and I wonder if there are conflicting npm directories from running npm start --production from the command line versus the /usr/local/bin/npm start --production in ghost in init.d which is what I believe service ghost *command* runs from. Is this possible?

    ghost:

    description "Ghost: Just a blogging platform"
    
    start on (local-filesystems and net-device-up IFACE=eth0)
    stop on runlevel [!12345]
    
    # If the process quits unexpectadly trigger a respawn
    respawn
    
    setuid ghost
    setgid ghost
    env NODE_ENV=production
    chdir /var/www/ghost
    
    exec /usr/local/bin/npm start --production
    
    pre-stop exec /usr/local/bin/npm stop --production
    
    • @connordphillips

      I'd need to see your NGINX server block as well. Since we need to setup NGINX to act as a reverse proxy, how it's setup is just as important as getting Ghost to respond to the request it proxies.

      Also, if you can post your config.js file for Ghost. You can strip out anything that's private, just use filler as a replacement.

      • Is this what you need?

        // # Ghost Configuration
        // Setup your Ghost install for various environments
        // Documentation can be found at http://support.ghost.org/config/
        
        var path = require('path'),
            config;
        
        config = {
            // ### Production
            // When running Ghost in the wild, use the production environment
            // Configure your URL and mail settings here
            production: {
                url: 'http://www.test-site.com',
                mail: {},
                database: {
                    client: 'sqlite3',
                    connection: {
                        filename: path.join(__dirname, '/content/data/ghost.db')
                    },
                    debug: false
                },
        
                server: {
                    // Host to be passed to node's `net.Server#listen()`
                    host: '127.0.0.1',
                    // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
                    port: '2368'
                },
        
                storage: {
                active: 'ghost-s3',
                'ghost-s3': {
                 accessKeyId: process.env.AWS_ACCESS_KEY,
                 secretAccessKey: process.env.AWS_SECRET_KEY,
                 bucket: process.env.S3_BUCKET,
                 region: process.env.S3_REGION,
                 assetHost: process.env.S3_URL
                }
                },
        
            },
        

@connordphillips

I also need to check your NGINX server block, which would be located in:

/etc/nginx/sites-available

or

/etc/nginx/sites-enabled

If those directories do not exist, you'd need to locate where your server blocks are for your domains.

A server block will look like:

server {
    listen ...;
    ...
    ...
    ...
}

... where ... is the configuration contents. Since NGINX is what's going to be handling requests on port 80, the default port you connect to when accessing http://youdomain.com, we need to make sure NGINX is setup to proxy requests on Port 80 to your Ghost installation.

If NGINX isn't setup, the only way you're going to be able to access Ghost is by running it with a Port attached to the URL.

Ghost config.js

The line below should be your actual domain name:

url: 'http://www.test-site.com'

and the following block is what I needed to help you configure NGINX properly:

        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        },

The above defines how Ghost runs the production server. With the above, the only way you're going to be able to connect to Ghost is by proxying, which we can do with NGINX, once I see what you've got right now :-).

  • I don't have www.test-site.com in my actual domain config. Just using a pseudo name for generalization here. Here is my ghost nginx config. I have a redirect to only use www.test-site.com which is still happening despite the 502.

    server {
        listen 80;
        server_name test-site.com; # Replace with your domain
    
        return 301 http://www.test-site.com$request_uri;
    
    }
    
    server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;
    
        server_name www.test-site.com;
    
        client_max_body_size 10G;
    
        location / {
            proxy_pass http://localhost:2368;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_buffering off;
        }
    }
    
    • Out of curiosity, going back to the upstart script differences, do you think the reference to what I believe is the old node path in that script /usr/local/bin/npm start --production as opposed to the updated version of node and npm located at /root/.nvm/v4.7.3/bin/npm could cause a conflict? Or would that be unrelated to the error presented?

      • @connordphillips

        The issue, from what I tested and mentioned in my more detailed response below, is not the script, but a slight configuration variance which results in the 503 error.

        When you run node ...., the version of node that will be used is the one that is reported when you run:

        which node
        

        In the init script for Ghost, if the above reports something other than /usr/bin/node, then you'd need to change this line in the init script from:

        DAEMON=/usr/bin/node
        

        to what is provided by the which command.

Have another answer? Share your knowledge.