Programmatic Creation of the Ghost Admin User

Problem

You want to completely automate the installation of Ghost including creation of the admin user?

You search on Google and land on this exactly worded forum post from 2019 with no ready-to-use answer.

Solution

You can do it using curl:

curl 'http://localhost:2368/ghost/api/admin/authentication/setup/' \
-X 'POST' \
-H 'Content-Type: application/json; charset=UTF-8' \
-H 'Accept: application/json, text/javascript, */*; q=0.01' \
-H 'Host: localhost:2368' \
-H 'Origin: http://localhost:2368' \
-H 'Referer: http://localhost:2368/ghost/' \
-H 'Connection: keep-alive' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'App-Pragma: no-cache' \
--data-binary '{"setup":[{"name":"Admin","email":"email@ghost.example.com","password":"<ghost-admin-password>","blogTitle":"Blog Title"}]}'

You only need to change the following values in the payload:

  • email;
  • password,
  • blogTitle.

This solution was used in a [:larger article] so you may want to read it as well.

PostScript

The Ghost CTO did offer the following suggestion, 2 years later:

You can automate booting Ghost to initialise the DB and then calling the setup API endpoint to finalise setup so that the window of opportunity is a few ms.

I did all of my research on a server with a public IP, and since it would take me more than a few ms before I could figure out how to correctly create the admin user programmatically, I had to take some precautions.

Rather than expose the unfinished Ghost Admin setup page over the public Internet, I decided to leave it accessible only from localhost and used SSH tunneling to access it from my machine.

To figure out the right incantations to correctly create an admin user from the CLI using curl, I installed a development instance of Ghost:

su - ghost-mgr
sudo mkdir -p /var/www/local && cd /var/www/local
sudo chown -R ghost-mgr:ghost-mgr /var/www/local
ghost install local

This will create an entry in ~/.ghost/config:

{
  "instances": {
    "ghost1-ayewo-com": {
      "cwd": "/var/www/ghost"
    },
    "ghost-local": {
      "cwd": "/var/www/local"
    }
  }
}

Each time you use the ghost-cli to interact with one or more instances of Ghost on your machine, it will update its entry inside /home/ghost-mgr/.ghost/config:

ghost ls
┌──────────────────┬────────────────┬─────────┬─────────┬─────┬──────┬─────────────────┐
│ Name             │ Location       │ Version │ Status  │ URL │ Port │ Process Manager │
├──────────────────┼────────────────┼─────────┼─────────┼─────┼──────┼─────────────────┤
│ ghost1-ayewo-com │ /var/www/ghost │ 5.46.1  │ stopped │ n/a │ n/a  │ n/a             │
├──────────────────┼────────────────┼─────────┼─────────┼─────┼──────┼─────────────────┤
│ ghost-local      │ /var/www/local │ 5.51.2  │ stopped │ n/a │ n/a  │ n/a             │
└──────────────────┴────────────────┴─────────┴─────────┴─────┴──────┴─────────────────┘

After figuring out the right curl incantations, I tested it against the development instance of Ghost and quickly encountered a bug where Ghost couldn't write to the SQLite database due to some file permissions issue.

So I tried this quick fix since Ghost in production requires this folder to be owned by a non-privileged user ghost (not ghost-mgr):

sudo chown -R ghost:ghost /var/www/local/content/

Ultimately I reverted it because the development instance of Ghost complained:

✖ Starting Ghost: ghost-local
A SystemError occurred.

Message: The content folder is not owned by the current user.
Ensure the content folder has correct permissions and try again.

The error went away after a ghost stop/ghost start cycle (I think?), so a workaround might be to simply restart Ghost immediately after installation of the development instance i.e.:

# after you install a development instance with
ghost install local

# restart it before continuing
ghost restart ghost-local

Your admin interface is located at:

    http://localhost:2368/ghost/

Note: The presence of this development instance will always cause Ghost to assume any attempts to run ghost start inside /var/www/ghost (the production instance path) or even invoking the name of the production instance explicitly (e.g. cd /var/www/ghost && ghost start ghost1-ayewo-com), is an attempt at running the development instance installed at /var/www/local.

Each time I tried to start the production instance while the development instance was still installed, this terrible assumption caused Ghost to output an inscrutable error even when the JSON at /var/www/ghost/config.production.json is perfectly valid:

1) Validating config

Error detected in the development configuration.

Message: Config file is not valid JSON

It always prefaced its outputs with "Running in development mode" even when the config file inside /var/www/ghost is explicitly named config.production.json and not config.development.json.

The fix was to relocate the development instance folder /var/www/local to another location where the ghost-cli cannot find it before continuing.