Switching from root to ghost user; forever and database permissions issue

The situation

  1. You’ve installed ghost on your server (in my case centos)
  2. You’ve installed forever
  3. You’re running ghost through forever as root.
  4. The only problem is you don’t want to run it as root as it is not a good idea to be running ghost, or any public app as root or with root priveleges. It is best to make another user who will run ghost. We will name such a user ghost.

So we leave forever running ghost in root and do a quick

$su ghost
$forever start index.js

Expecting everything to work as before but instead I get hit with one of these:

warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: /var/www/ghost/index.js

fs.js:427
    return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
             ^
Error: ENOENT, no such file or directory '/home/ghost/.forever/2hWg.log'
    at Object.fs.openSync (fs.js:427:18)
    at Object.forever.startDaemon (/usr/lib/node_modules/forever/lib/forever.js:406:14)
    at /usr/lib/node_modules/forever/lib/forever/cli.js:257:13
    at /usr/lib/node_modules/forever/lib/forever/cli.js:144:5
    at /usr/lib/node_modules/forever/lib/forever.js:358:11
    at Object.oncomplete (fs.js:107:15)

Argh! What’s this. Let’s try something else:

$forever list
/usr/lib/node_modules/forever/lib/forever.js:674
    procs.forEach(function (proc) {
      ^
TypeError: Object Error: EACCES, mkdir '/home/ghost' has no method 'forEach'
    at Object.forever.format (/usr/lib/node_modules/forever/lib/forever.js:674:11)
    at /usr/lib/node_modules/forever/lib/forever.js:511:28
    at /usr/lib/node_modules/forever/lib/forever.js:136:14
    at /usr/lib/node_modules/forever/lib/forever.js:89:20
    at /usr/lib/node_modules/forever/node_modules/utile/node_modules/mkdirp/index.js:25:29
    at /usr/lib/node_modules/forever/node_modules/utile/node_modules/mkdirp/index.js:25:29
    at /usr/lib/node_modules/forever/node_modules/utile/node_modules/mkdirp/index.js:37:53
    at Object.oncomplete (fs.js:107:15)

Ok fine let’s try just node and see if maybe the problem is in forever

$node index.js
events.js:72
    throw er; // Unhandled 'error' event
              ^
Error: listen EADDRINUSE
    at errnoException (net.js:904:11)
    at Server._listen2 (net.js:1042:14)
    at listen (net.js:1064:10)
    at net.js:1146:9
    at dns.js:72:18
    at process._tickDomainCallback (node.js:459:13)
    at process._tickFromSpinner (node.js:390:15)

NOOO, even node isn’t working… but wait, the error looks a bit different now: EADDRINUSE. We forgot to turn off ghost as root. let’s go back and do that then.

$exit
$forever stopall
info:    Forever stopped processes:
data:        uid  command       script                  forever pid logfile                 uptime        
data:    [0] EFde /usr/bin/node /var/www/ghost/index.js 709     711 /root/.forever/EFde.log 0:4:51:46.864 
$forever list
info:    No forever processes running

Ok we are good to try again now.

$su ghost
$node index.js
Ghost is running in development... 
Listening on YOUR.IP:2368 
Url configured as: http://my-ghost-blog.com 
Ctrl+C to shut down

Great. Let’s exit that and try forever again. We do the suggested Ctrl+C then run forever and … we get the same error as before. Argh! We are too flustered to give the error message more than a fleeting glance, do they actually tell us anything important? Ok fine, but just a quick glance, if we don’t look at the error hard it won’t be a hard error to fix… we secretly hope. The errors are centered around the directory /home/ghost. Let’s head over there and do some investigating into permissions.

$cd /home/ghost
bash: cd: /home/ghost: No such file or directory

Whoaaa it doesn’t even exist. Now that would definetely be a problem. What happened? I know ghost is a user, how come no home directory was made? Play dramatic music

The Reason

If you were following the ghost docs (great job), and you wanted to try the init option for starting ghost on startup then you would have executed the following commands:

$useradd -r ghost -U
$chown -R ghost:ghost /var/www/ghost
$chmod 755 /etc/init.d/ghost

Of course when I came to run the ubuntu script on centos I ran into many errors, but that is another post for another time. Anyways, the point is I have my ghost user now so there was no need to make a new one. The problem, however was in useradd -r ghost -U. I had taken a command from a different context and was hoping it would work in a the new context. Let’s break it down so we understand better.

From the man useradd command we find the following.

-r, --system
       Create a system account.

       System users will be created with no aging information in /etc/shadow, and their numeric identifiers are choosen in
       the SYS_UID_MIN-SYS_UID_MAX range, defined in /etc/login.defs, instead of UID_MIN-UID_MAX (and their GID
       counterparts for the creation of groups).

       Note that useradd will not create a home directory for such an user, regardless of the default setting in
       /etc/login.defs (CREATE_HOME). You have to specify the -m options if you want a home directory for a system account
       to be created.

That last note is so critical. A home directory is not made because it is a system account. So what is a system account? Well I found this from gentoo wiki.

To enforce the separation of privileges, specific system accounts are created for each task. If you run a mail server on your system, that mail server will have a user account on your system.

These accounts are not usable by regular users: you can not log in on your system using those accounts. They exist only to allow the specific processes to run with their own permissions and privileges.

Ah so that’s what a system user is. Since another name for a home directory is the login directory (linfo.org), it makes sense that no home directory should be made for a system user as no one is meant to login using that account. So the ghost system account that was made earlier was supposed to run ghost, and it is indeed able to run ghost as we saw earlier. But what we tried to do was run forever and that’s where we hit the error. It turns out that forever uses the home directory to put its logs, and when it tries to do that it can’t because there is no such directory and ghost user does not have the privelege to make the directory (change anything in /home).

The Solution

Give ghost user a home directory

We can accomplish this in two ways.

Make the ghost user home directory manually

Let’s go back to root or a sudo enabled account and let’s execute the following commands.

$mkdir /home/ghost
$ls -l /home/
drwx------ 3 auser       auser       4096 Dec 31 13:51 auser
drwxr-xr-x 2 root        root        4096 Mar 31 12:13 ghost
drwx------ 2 buser       buser       4096 Nov 11 23:31 buser

So we now have a ghost home directory but the persmissions and ownership are all wrong, as can be seen from the ls -l command. We remedy that by doing the following:

$chown ghost:ghost ghost
$chmod 700 /home/ghost
$chown ghost:ghost ghost
$ls -l
drwx------ 3 auser       auser       4096 Dec 31 13:51 auser
drwx------ 2 ghost       ghost       4096 Mar 31 12:13 ghost
drwx------ 2 buser       buser       4096 Nov 11 23:31 buser

Now it looks proper again. Here’s a quick break down. With the chmod we made the permissions of the directory the same as the others. With the chown we made the user ghost and the group ghost owners of the directory ghost. Not confusing at all.

Delete ghost user and make him again the right way

Let’s just burn it all down and start again. First delete the user as root or a sudoer:

$userdel ghost

Note if you had already created the home root and want to try this way anyway add an r flag so it looks like this, userdel -r ghost

I’d be interested in seeing what happened to the ownership of the ghost directory. If you remember we had issued this command, $chown -R ghost:ghost /var/www/ghost earlier which gave the, now non-existant, ghost user ownership of the ghost directory and its contents. Let’s take a look at it now:

$ls -l /var/www/
drwxr-xr-x  6  498  498 4096 Apr  3 10:13 ghost
drwxr-xr-x 12 root root 4096 Nov 13 02:20 html

Where the name ghost used to be there are now the numbers 498. That was the UID (user ID) of ghost.

Now we add him again with the extra m flag.

$useradd -rm ghost -U

Check that the home directory was made:

$ls /home | grep ghost
ghost

Yup we have it. Now let’s check what happened to the ownership of the ghost directory:

$ls -l /var/www/
drwxr-xr-x  6 ghost ghost 4096 Apr  3 10:13 ghost
drwxr-xr-x 12 root  root  4096 Nov 13 02:20 html

Looks like we were in luck and the new ghost had the same UID as the old one, so we don’t need to fix that.

Change the forever root (Not recommended because it is buggy as of version0.10.11)

First we need to figure out where the forever root is configured. Looking at the documentation of forever, I can’t seem to find what I am looking for. Time to take a look at the code. It just so happens that as I am looking at the github repo I notice that srossross had committed a fix where he added a FOREVER_ROOT variable. Great that’s just what we need. Only problem is it is so recent that the update is not available for update yet. As of version 0.10.11 it hadn’t been added yet. You can check the version you have using the following:

$npm list | grep forever@
forever@0.10.11 /usr/lib/node_modules/forever

So we will have to do this manually and change it ourselves. It is only one line so shouldn’t be too scary.

$vi /usr/lib/node_modules/forever/lib/forever.js

Go to the line that says something like,

forever.root = path.join(process.env.HOME || process.env.USERPROFILE || '/root', '.forever');

And just change it to the following:

forever.root         = process.env.FOREVER_ROOT || path.join(process.env.HOME || process.env.USERPROFILE || '/root', '.forever');

Press ESC or in some cases Ctrl+c then :x to save in vi. Now let’s run the command with the new root as follows:

NODE_ENV=production FOREVER_ROOT=/var/www/ghost/forever forever start var/www/ghostindex.js

Now forever won’t need to have the home directory. As for the bug, you can check my issue I reported in github.

Testing the Solution

Now that I’ve solved the problem above, let’s test it out. Switch to the ghost user and run ghost as follows:

$su ghost
$forever start /var/www/ghost/index.js
warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: /var/www/ghost/index.js

Let’s check the log file just in case. We can see the log file by listing the forever processes as follows:

$ forever list
info:    No forever processes running

NOOOO, how come? I just saw it saying Forever processing file: /var/www/ghost/index.js. Wait a sec, I think I’m in the wrong place.

$pwd
/root

Argh that’s why. I was in that directory as root and when switched to ghost it kept me there. Let’s go to somewhere more comfortable.

$cd
$pwd
/home/ghost

Alright try running it one more time and this time we do get a list.

info:    Forever processes running
data:        uid  command       script                 forever pid logfile                       uptime      
data:    [0] 7z38 /usr/bin/node /var/www/ghost/index.j 791     793 /home/ghost/.forever/7z38.log 0:0:0:5.952

Do a quick cat on that logfile and make sure its looking good

$cat /home/ghost/.forever/7z38.log
Ghost is running in development... 
Listening on 192.241.121.253:2368 
Url configured as: http://my-ghost-blog.com 
Ctrl+C to shut down

That looks nice. Ok now let’s take a look at our blog and NOOOO all my old posts are gone. What happened? Did we lose all our content? Actually we ran it in development instead of production. Stop the process and run it again in development as follows:

$forever stopall
info:    Forever stopped processes:
data:        uid  command       script                  forever pid  logfile                 uptime      
data:    [0] 7z38 /usr/bin/node /var/www/ghost/index.js 791     793 /root/.forever/7z38.log 0:0:0:9.299
$NODE_ENV=production forever start /var/www/ghost/index.js
warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: /var/www/ghost/index.js

Ok good that looks proper. Time to check the log file again. List and cat as before and we get:

ERROR: SQLITE_READONLY: attempt to write a readonly database, sql: update "settings" set "created_at" = ?, "created_by" = ?, "id" = ?, "key" = ?, "type" = ?, "updated_at" = ?, "updated_by" = ?, "uuid" = ?, "value" = ? where "id" = ?, bindings: Fri Mar 28 2014 21:55:58 GMT-0400 (EDT),1,16,installedApps,app,Thu Apr 03 2014 07:16:44 GMT-0400 (EDT),1,792297d0-2813-4e91-850e-578f38c76328,[],16 

 Error: SQLITE_READONLY: attempt to write a readonly database, sql: update "settings" set "created_at" = ?, "created_by" = ?, "id" = ?, "key" = ?, "type" = ?, "updated_at" = ?, "updated_by" = ?, "uuid" = ?, "value" = ? where "id" = ?, bindings: Fri Mar 28 2014 21:55:58 GMT-0400 (EDT),1,16,installedApps,app,Thu Apr 03 2014 07:16:44 GMT-0400 (EDT),1,792297d0-2813-4e91-850e-578f38c76328,[],16
    at /var/www/ghost/node_modules/knex/clients/server/base.js:73:22
    at tryCatch1 (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/util.js:64:19)
    at Promise$_callHandler [as _callHandler] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:708:13)
    at Promise$_settlePromiseFromHandler [as _settlePromiseFromHandler] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:724:18)
    at Promise$_settlePromiseAt [as _settlePromiseAt] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:896:14)
    at Promise$_rejectPromises [as _rejectPromises] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:1033:14)
    at Async$_consumeFunctionBuffer [as _consumeFunctionBuffer] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/async.js:64:12)
    at Async$consumeFunctionBuffer (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/async.js:37:14)
    at Promise$_Scheduler (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/schedule.js:40:58)
    at process._tickDomainCallback (node.js:459:13) 


ERROR: SQLITE_READONLY: attempt to write a readonly database, sql: update "settings" set "created_at" = ?, "created_by" = ?, "id" = ?, "key" = ?, "type" = ?, "updated_at" = ?, "updated_by" = ?, "uuid" = ?, "value" = ? where "id" = ?, bindings: Fri Mar 28 2014 21:55:58 GMT-0400 (EDT),1,16,installedApps,app,Thu Apr 03 2014 07:16:44 GMT-0400 (EDT),1,792297d0-2813-4e91-850e-578f38c76328,[],16 
 The app will not be loaded 
 Check with the app creator, or read the app documentation for more details on app requirements 

Ghost is running... 
Your blog is now available on http://blog.amayem.com 
Ctrl+C to shut down
Possibly unhandled Error: SQLITE_READONLY: attempt to write a readonly database, sql: delete from "sessions" where "id" = ?, bindings: HwxKR0LJRcWG39FcT1ZwkMyq
    at /var/www/ghost/node_modules/knex/clients/server/base.js:73:22
    at tryCatch1 (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/util.js:64:19)
    at Promise$_callHandler [as _callHandler] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:708:13)
    at Promise$_settlePromiseFromHandler [as _settlePromiseFromHandler] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:724:18)
    at Promise$_settlePromiseAt [as _settlePromiseAt] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:896:14)
    at Promise$_rejectPromises [as _rejectPromises] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/promise.js:1033:14)
    at Async$_consumeFunctionBuffer [as _consumeFunctionBuffer] (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/async.js:64:12)
    at Async$consumeFunctionBuffer (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/async.js:37:14)
    at Promise$_Scheduler (/var/www/ghost/node_modules/knex/node_modules/bluebird/js/main/schedule.js:40:58)
    at process._tickDomainCallback (node.js:459:13) 

NOOOO, more errors. But it was fine in development. Notice that in the middle of the errors we got a cool Ghost is running…, but of course we can’t continue with such an error. Let’s, grudingly, take a look at the error. Looks like a database issue, something is trying to write in a databse that is readonly. It could be forever or ghost, but judging by the path of the node modules where the error originated, /var/www/ghost/node_modules/knex/... it looks like ghost. There is an easy way to figure this out. Let’s run ghost without forever as follows (stop the forever process first though):

$NODE_ENV=production node /var/www/ghost/index.js

Aaaand we get the same error. Ok good so now we know it’s a ghost issue this time. Let’s go to this database and see its permissions:

$cd /var/www/ghost/content/data/
$ls -l
total 80
-rw-r--r-- 1 root  root  35840 Apr  1 15:15 ghost.db
-rw-r--r-- 1 ghost ghost 31744 Apr  3 07:43 ghost-dev.db
-rw-r--r-- 1 ghost ghost   121 Mar 26 18:39 README.md

Aha! We found it. ghost.db is owned by root. The reason for that was that we had originally started ghost in production in root so the database was made with that ownership. The dev database was made in ghost because that was the first time I had run it in dev so there were no problems. The solution is simply to change the ownership from root or as a sudoer with sudo:

$chown ghost:ghost ghost.db
$ls -l
total 80
-rw-r--r-- 1 ghost ghost 35840 Apr  1 15:15 ghost.db
-rw-r--r-- 1 ghost ghost 31744 Apr  3 07:47 ghost-dev.db
-rw-r--r-- 1 ghost ghost   121 Mar 26 18:39 README.md

That looks much better. Let’s test again:

NODE_ENV=production node /var/www/ghost/index.js 
Ghost is running... 
Your blog is now available on http://blog.amayem.com 
Ctrl+C to shut down

No more error and my posts are back. Solved!

References

  1. Ghost deployment docs
  2. Forever github repo
  3. linfo.org
  4. gentoo wiki

Ahmed Amayem has written 90 articles

A Web Application Developer Entrepreneur.

  • Sparky Doosan

    that is great stuff man… explains the problem, explains what to do about it, explains the WHAT and WHY

    • http://ahmed.amayem.com ahmedamayem

      Thanks man! Happy it helped!