I'll start out by stating the obvious: I am not a web developer. Hell, I don't even play one on T.V.! At one point in my career, though, I could have been had I decided to embrace the world of front-end / UI Engineer, but my career took me down another path (User Experience, Customer Research, and Product Design).
I wrote a rather large post about rebooting my digital footprint in January of 2022. I had ditched WordPress for Ghost, which I chose to self-host at Digital Ocean on a 1-click droplet. It was easy enough, right? Push a button, pick a server, and let it rip!
But, me being me, I wanted to find something more complicated. I got curious about spinning up my own Ubuntu VPS with a cloud provider - mostly to see if I could get max performance out of a like $2/mo box - but also to see if I had the technical and analytical skills to actually do it.
You're reading this post on my new CloudFanatic (affiliate link), $6.99/mo VPS based out of Chicago, IL - just 300 miles north of St. Louis. I've got 4GB RAM, 2vCPU cores, and a 50GB SSD to play with for all the things I want to run.
Remember how I'm not a web developer? I do have some familiarity with command line and I'm getting increasingly comfortable with the idea of moving around Linux without a GUI. This old designer is learning new tricks!
The first thing I had to do was
ssh in to the VPS as the root user:
ssh root@IP Address
Now that I'm in the box, I had to create a new user and give it appropriate permissions:
# adduser ghost-mgr # usermod -aG sudo ghost-mgr
Next, I needed to log in as that new user:
# su - ghost-mgr
Now that I'm logged in as
ghost-mgr, it's time to upgrade my install of Ubuntu 22.04:
$ sudo apt update && sudo apt upgrade -y
Next, I needed to install nginx:
$ sudo apt install nginx
Just for good measure, I wanted to verify that everything was ok with nginx by running the following command:
$ sudo systemctl status nginx
nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) Docs: man:nginx(8) Process: 66019 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Process: 66020 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Main PID: 66112 (nginx) Tasks: 2 (limit: 2196) Memory: 2.6M CPU: 148ms CGroup: /system.slice/nginx.service ├─66112 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;" └─66113 "nginx: worker process"
Looks good enough to me (but again, I am not a developer).
Now's where things got a little complicated for me. It was time to install MySQL. I am - again - barely comfortable with node.js and certainly not as comfortable with SQL as I'd like to admit, but I'm following the guide that was published by Ghost on how to self-install, so I went in with gusto. Worst-case scenario, it doesn't work and I have to go back and try again... and again... and again.
$ sudo apt install mysql-server
Success. Now, I needed to start it up and make sure that if I rebooted the VPS, it'd also automatically start (
enable, in the parlance of MySQL):
$ sudo systemctl start mysql $ sudo systemctl enable mysql
Just for shits 'n giggles, I wanted to test to see if it was working by running the following command:
$ sudo systemctl status mysql
And the output?
mysql.service - MySQL Community Server Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled) Active: active (running) Main PID: 1083 (mysqld) Status: "Server is operational" Tasks: 41 (limit: 2797) Memory: 434.0M CPU: 1min 57.932s CGroup: /system.slice/mysql.service └─1083 /usr/sbin/mysqld
Again, looks good to me. It actually output something!
A lot of web searching tells me that MySQL is actually not hardened or secure out of the box. So, I ran the following command:
$ sudo mysql_secure_installation
And configured as follows:
- Set root password? [Y/n] Y - Remove anonymous users? [Y/n] Y - Disallow root login remotely? [Y/n] Y - Remove test database and access to it? [Y/n] Y - Reload privilege tables now? [Y/n] Y
At this point, I was just hoping that I didn't mess it up. It was now time to create the MySQL database, add a user, and give that user permissions:
$ sudo mysql -u root -p
Success, baby! I'm now seeing a
mysql> prompt instead of my usual
$ prompt. I am truly hacking the planet. Time to get after it:
mysql> CREATE DATABASE ghostdb; mysql> CREATE USER 'ghostuser'@'localhost' IDENTIFIED BY 'PASSWORD_HERE'; mysql> GRANT ALL PRIVILEGES ON ghostdb. * TO 'ghostuser'@'localhost'; mysql> FLUSH PRIVILEGES; mysql> exit;
Time to remember some things, because the MySQL database that makes my Ghost install run is not the same as the
root user, with a different password, or my
ghost-mgr user with yet another password. It's really easy to get lost in the sauce, so write some of this stuff down so you don't regret it!
Now that we've exited the database set-up portion of the install, we need to toss Node.js on the VPS. We can do that pretty easily by banging out the following:
$ sudo curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash
You'll get some errors that whine about "oh, no! this is outdated, blah blah blah" - just blow through those. Keep on truckin' - we're nearly there.
After your screen looks like it's glitching in The Matrix, you'll need to actually install Node.js:
$ sudo apt-get install -y nodejs
And, much like we've done before, you can verify the installed version with a simple command:
With an output of:
Now, we need to install Node Package Manager (
npm), which is the engine that drives a lot of our Ghost install:
$ sudo npm install npm@latest -g
And, again, to verify:
npm --version npm: '8.19.4'
It's finally time to do the thing. You've made it this far...
Now we need to install the Ghost command-line interface (CLI) tool. This will allow us to actually install Ghost. Can you feel the excitement? I sure could:
$ sudo npm install -g ghost-cli@latest
And, once again, we can check to see what version we have:
$ ghost -v Ghost-CLI version: 1.25.3
Smells like success.
Now we get to finally - finally! - install Ghost and see all of this hard work pay off.
First, we make a new directory, grant our
ghost-mgr user permission to the directory, set up permissions, and move inside the directory we created:
$ sudo mkdir /var/www/ghost $ sudo chown ghost-mgr:ghost-mgr /var/www/ghost $ sudo chmod 775 /var/www/ghost cd /var/www/ghost/
Drumroll, please. It's now time to press the button and do the thing!
$ ghost install
When done successfully, you'll see the following output:
✔ Checking system Node.js version - found v16.18.1 ✔ Checking logged in user ✔ Checking current folder permissions ✔ Checking system compatibility ✔ Checking for a MySQL installation ✔ Checking memory availability ✔ Checking free space ✔ Checking for latest Ghost version ✔ Setting up install directory ✔ Downloading and installing Ghost v5.24.0 ✔ Finishing install process
And then, you'll get to actually configure the install!
? Enter your blog URL: http://yourdomain.com ? Enter your MySQL hostname: localhost ? Enter your MySQL username: ghostuser ? Enter your MySQL password: [hidden] ? Enter your Ghost database name: ghostdb ✔ Configuring Ghost ✔ Setting up instance + sudo useradd --system --user-group ghost ? Sudo Password [hidden] + sudo chown -R ghost:ghost /var/www/ghost/content ✔ Setting up "ghost" system user ℹ Setting up "ghost" mysql user [skipped] ? Do you wish to set up Nginx? Yes + sudo nginx -s reload ✔ Setting up Nginx
You'll also get to do a few other things with the install – mainly, set up your free Let's Encrypt SSL certificate for
https all over the place, and set up
systemd to keep ghost running smoothly. I promise you it's worth it.
And once you've got all that done, you can finally - at long last! - log-in via the web in the URL provided.
Pour yourself a drink, partner. You've earned it!