Creating a WordPress blog on Ubuntu

I’d like to dedicate the first post of this blog to how the blog was created. As a developer, I like to have full control and tinker with the code. WordPress is my top choice because it’s open-source, provides the most flexibility, and allows me to set up an instance on a server easily. Still, I encountered some issues along the way and shared my notes here. Alternatively, WordPress.com is another way to create a WordPress blog.

Setup

The only requirements are a server with root access and a domain.

  • Machine: I have a Ubuntu instance with DigitalOcean, 5 USD per month
  • Web server: on the Ubuntu instance, Nginx (free, open-source) is used for serving the WordPress blog (and potentially other services)
  • Domain: I use Google Domains, my domains are all 12 USD per year
  • (Recommended) Encryption: in order to enable HTTPS for secure connections, I use Let’s Encrypt (free, non-profit) which is sufficient for a personal site

Domain: create a DNS record

This step can be done anytime before launching the blog for the first time. Depending on the domain provider, a DNS record can be created to serve the blog. The DNS record corresponds to the URL of the blog like geminstall.com, and could be a subdomain like code.geminstall.com. For subdomain, the DNS record has type A and the data is the IP address of the server.

Install WordPress

  • SSH to the server: ssh username@server_IP
  • Go to a directory that contains the WordPress directory (could be anywhere, the routing is set up later with Nginx)
  • Follow the detailed instructions to download and extract WordPress

Afterward, a wordpress directory is created at the directory in step 2 above.

Create a database with MySQL

MySQL is an open-source database management system, and I have used it to set up databases for multiple WordPress sites.

  • If the server doesn’t have MySQL yet, install it on the server machine first. DigitalOcean has a post on how to install MySQL on Ubuntu 20.04
  • Follow the official instructions to create a database with MySQL
    • The user names in the tutorial are MySQL users, not Ubuntu users. It’s considered best practice to have another MySQL user that is not root with access to the database.
    • If you have multiple WordPress sites or any existing databases, make sure to create one with a different name!
  • Edit wp-config.php with database information
    • Go to the directory with WordPress: cd wordpress
    • Copy the sample config file: cp wp-config-sample.php wp-config.php
    • Edit the config file to fill in database information: vim wp-config.php (or any editor you prefer). Mostly, update the database name, host (localhost), username, and password (from the MySQL user)

Nginx: set up a virtual host

I set up the Nginx according to the DigitialOcean guide on virtual hosts (one server machine serving multiple hosts like many WordPress sites and other services). If you’re familiar with Docker, it might be worth trying to install WordPress with Docker Compose (I haven’t tried it yet).

  • If the server doesn’t have Nginx set up yet, install it first (instructions for Ubuntu machines or DigitialOcean guide)
  • Create a new file under /etc/nginx/sites-available following the WordPress Nginx recipe (e.g. /etc/nginx/sites-available/wordpress_geminstall)
  • Symlink the WordPress Nginx recipe to /etc/nginx/sites-enabled so that we can disable a site by unlinking it later (e.g. sudo ln -fs /etc/nginx/sites-available/wordpress_geminstall /etc/nginx/sites-enabled/wordpress_geminstall)
  • Restart Nginx: sudo systemctl restart nginx

If the routing doesn’t work, you can check the Nginx error log at sudo tail -f /var/log/nginx/error.log

A blog is born

If all the previous steps are done, a WordPress installation page is going to show up at the URL! Make sure to complete the WordPress installation steps before another person or a bot sneaks in 😀 Also, if encryption isn’t set up yet, some browsers like Chrome might aggressively change a URL to the HTTPS version. If the installation page isn’t showing up, try making sure the URL is HTTP instead of HTTPS.

(Optional but recommended) Encryption with Let’s Encrypt

For a personal blog, Let’s Encrypt is most likely sufficient and it’s super easy to set up. It’s a non-profit organization and the installation with Certbot is the easiest of all steps. Since we are setting up a blog on a Ubuntu machine with root access, we are going with the setup flow with shell access. Before setting up encryption, we can first make sure the HTTP version works.

On the Certbot homepage, select Nginx and the OS version of the server (Ubuntu 16.04 for me). When running sudo certbot --nginx, it shows a list of URLs that are handled by Nginx and you can select one or multiple. Under the hood, Certbox added some code to the /etc/nginx/sites-available/* file. When prompted to choose whether or not to redirect HTTP traffic to HTTPS (removing HTTP access), it’s recommended to choose yes to always serve with HTTPS.

A secure blog is born

After adding HTTPS support, the blog is securely online now! I hope you don’t run into unexpected errors or issues, but it’s totally normal if that’s the case. The server can have some pre-existing setup and different tooling versions. This setup has been working well for two years, but I’m curious to try Docker if I am to start a blog on a new server again.

Appendix: Enabling WordPress.com

When I was installing and activating Jetpack to enable WordPress.com connection for WordPress mobile apps, I got a -37200 error parsing XML-RPC response:

curl -A 'Jetpack by WordPress.com' -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>' https://geminstall.com/xmlrpc.php
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <fault>
    <value>
      <struct>
        <member>
          <name>faultCode</name>
          <value><int>-32700</int></value>
        </member>
        <member>
          <name>faultString</name>
          <value><string>parse error. not well formed</string></value>
        </member>
      </struct>
    </value>
  </fault>
</methodResponse>

After searching around, I fixed it by installing php-xml with the following command from this StackOverflow answer:

sudo apt-get install php-xml