EDIT 2019-05-22

This post was hosted on a Raspberry Pi at the original time of writing, but now is hosted on Github. See this post.

Every linux flavor has its own path to success

And yes, this blog is running on a Raspberry Pi.

There are issues with the version of Node.js v0.10.29 that comes preinstalled with Raspbian Jessie 2015-11-21 or newer. It’s not possible to install Node.js native add-ons or packages that depend on native add-ons. Therefore, Node must be built from source.

As a note, both building Node and installing Ghost require many things to be run as root, so it’s best to switch to the root user before beginning.

  1. Fetch the latest version of Node $ wget http://nodejs.org/dist/node-latest.tar.gz

  2. Unpack the compressed tarball $ tar -xzf node-latest.tar.gz

  3. Change to the unpacked directory, for example $ cd node-v6.2.2/
    where the version number will vary.

  4. Configure your makefile $ ./configure

    If you’re on a Raspberry Pi 2 or 3, it’s best to pause at this point and ensure that you’re utilizing all four cores during the make process. By default, make only utilizes one core. In the real world, this means that building Node from source using one core will take several hours to complete. Using all four cores on a Pi 2 reduces the time needed by about 75%, resulting in the build process only taking about an hour to complete.

    Using multiple cores during the make process can be as simple as passing the -j flag with the number of cores you wish to use, or it can be a bit more elegant and flexible by doing something like adding the lines below #Parallel Make to the end of your .bashrc file then reloading your profile with $ source ~/.bashrc.

    Here, export NUMCPUS=$() both defines the environment variable and sets its value to grep -c "^processor" /proc/cpuinfo where “grep” searches the file “/proc/cpuinfo” for lines that begin with “processor” and passing the “-c” flag to grep counts the number of results.

    The last line simply sets an alias called “pmake”, which is simply calling make with the necessary arguments to run multiple jobs in parallel.

    #Parallel Make
    export NUMCPUS=$(grep -c "^processor" /proc/cpuinfo)
    alias pmake='time nice make -j$NUMCPUS --load-average=$NUMCPUS'
    
  5. Make Node using one of the following

    • $ make => Simply uses one core, or alternately
    • $ make -j4 -l4 => Manually configure make to use all four cores and ensure that only four jobs are running simultaneously, or alternately
    • $pmake => Use the alias created above to have make use all cores and time the process.
  6. Install Node $ make install
    Note: Installation is a quick process, so there’s no need to run things in parallel.

  7. Download the latest version of Ghost $ wget https://ghost.org/zip/ghost-latest.zip

  8. Unzip Ghost $ unzip -d /path/to/install/ghost ghost-latest.zip
    and change to the directory where the files were saved
    $ cd /path/to/install/ghost

  9. Install Ghost $ npm install --production

  10. Create a ghost configuration file by copying the example $ cp config.example.js config.js
    We’re not changing any of the defaults as a part of the installation, so the server will listen on port 2368 by default.
    Note: when installing the latest version of Node, the most recent version isn’t currently supported by Ghost. However, the most current version works as intended after disabling Ghost’s version check with an environment variable. This can be passed along with command to start Ghost or set in the bash profile. The necessary variable is GHOST_NODE_VERSION_CHECK=false.

  11. Start the Ghost process to ensure that the installation works $ npm start --production (if the environment variable is set in the bash profile) or alternately
    $ GHOST_NODE_VERSION_CHECK=false npm start --production (if the environment variable is to be passed along with the command)

  12. Check to see if the Ghost server is accessible by using a second terminal window to run $ curl -vso /dev/null http://127.0.0.1:2368
    where a “HTTP/1.1 200 OK” in the response headers for the curl request indicates a successful configuration and looking at the terminal for the running npm ghost process, there should be an access log line.

  13. To avoid the requirement of keeping a terminal window open to keep Ghost running, install Supervisor to run an app as a daemon $ apt-get update && apt-get install supervisor -y

  14. Create a configuration file for Supervisor to manage Ghost $ touch /etc/supervisor/conf.d/ghost.conf
    that contains the following lines

    [program:ghost]
    command = npm start --production
    directory = /path/
    user = root
    autostart = true
    autorestart = true
    stdout_logfile = /var/log/supervisor/ghost.log
    stderr_logfile = /var/log/supervisor/ghost_err.log
    environment = NODE_ENV="production",GHOST_NODE_VERSION_CHECK="false"
    
  15. Check to see if Supervisor is running after installation $ service supervisord status
    if Supervisor is not running
    $ service supervisord start

  16. Reload Supervisor with with the new Ghost configuration file $ supervisorctl reload
    and check to see if Ghost is running
    $ supervisorctl status
    If it isn’t
    $ supervisorctl start ghost

  17. Install Nginx to proxy Ghost and make it available externally $ apt-get update && apt-get install nginx-full -y

  18. Create a new virtual host file to proxy Ghost $ touch /etc/nginx/sites-available/ghost_website_name
    and create a symlink in the sites-enabled directory
    $ ln -s /etc/nginx/sites-available/ghost_website_name /etc/nginx/sites-enabled/ghost_website_name
    and delete the symlink for the default virtual host
    $ rm /etc/nginx/sites-enabled/default

  19. Edit the ghost_website_name virtual host file so that it contains the following

    server {
            listen          80;
            server_name     example.com *.example.com;
            location / {
                    proxy_pass      http://127.0.0.1:2368;
            }
    }
    
  20. Restart Nginx $ service nginx restart

  21. Ghost is now accessible at the external IP address where it was installed on port 80 (the default port for HTTP traffic). Visiting http://\{IP_address}/ in a browser will now load Ghost. I’ll save configuring Ghost and Nginx for a future post.

Quite a few more steps are needed than would be necessary on a more standard linux distribution (eg, Ubuntu) on more standard architecture (eg, x86) likely due to the technologies used not originally being developed for armhf and the user-base being much smaller so packaged versions of the above are less likely to be available and in some cases, not as thoroughly tested.


As an addendum, The theme I was using relied on Ghost loading jQuery before the one JS file used in the theme was loaded. In older versions of Ghost, this happened as intended. However, in the latest version, jQuery isn’t loaded before main.js, so default.hbs needs to be modified such that <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.0.min.js"></script> appears on the line immediately preceding <script type="text/javascript" src="/assets/js/main.js?v=e58334d1c1"></script> which allows main.js to function as intended.


Update: 2017-01-24

The SD card in my Raspberry Pi developed some bad sectors where the MBR was located on the disk, so it was no longer bootable and I’ve replaced it. Fortunately, I had an old export of most of my posts so I was able to recover almost everything. I’ll attempt to recover my most recent post, but for some reason, I don’t see it in the ghost.db, but a fragment is still in the Cloudflare cache, so while slightly confused, I retain a sliver of hope for recovery.

I changed one thing this go around— instead of using upstart, I decided to write a systemd service since the latest stable Debian release and stable derivatives all use systemd. It’s fairly straight-forward.

  1. Create the file ghost.service. Note: I realize I should probably not run the service as root, but as another user with adequate permissions.

    [Unit]
    Description=Ghost
    After=network.target
    
    [Service]
    Type=simple
    User=root
    WorkingDirectory=/path/to/ghost
    Environment="GHOST_NODE_VERSION_CHECK=false"
    ExecStart=/usr/local/bin/npm start --production
    Restart=on-abort
    
    [Install]
    WantedBy=multi-user.target
    
  2. Enable the service: sudo systemctl enable ghost.service

  3. Reload Systemctl: sudo systemctl daemon-reload

  4. Restart the Ghost service: sudo systemctl restart ghost.service

  5. Check the status to ensure that it’s running: sudo systemctl status ghost.service