Transition to Self-Hosted WordPress

I’ve had a number of domains which I had hosted blogs on in the past and that have been lying fallow. I was pretty happy hosting them on wordpress.com, but the number of sites and the monthly cost per site were just too high to justify the cost of WordPress hosting for sites that were simple “for fun” web publishing. I’ve made several attempts at various static-site rendering solutions or Netflify JAMstack implementations, and while I think they are truly awesome, they also require a lot of development and implementation maintenance (tooling versions are constantly evolving and there isn’t much guarantee that content will continue to work over the long haul). I’ve also lost content in previous engine transfers (mostly from exporting from hosted blog platforms that ran out of VC money), so I suppose I’d rather stay with WordPress(.org) if I can. Eventaully someone may create a static rendering pluging for the WordPress engine and hosting this will all be easier with free or cheap static page CDNs and the engine hosted privately, but so fare those solutions do not seem fully baked and well supported.

Essential System Setup

I started with a 1gb Digital Ocean droplet with Ubuntu 20.04 installed and weekly backups configured. I set up a new ssh key and added a ssh config file entry for connect to the host:

Host wordpress
    HostName 167.172.199.82
    User root

The host needed initial package updates:

apt update
apt upgrade
apt install net-tools

I changed the ssh port as well, to reduce amount of useless security probing and port scanning. This isn’t a huge security measure, but I understand that there a lot of unsophisticated nuisance attacks.

vi /etc/ssh/sshd_config
# Uncomment and modify "Port" directive
service sshd restart

This required an update to my ssh config file as well.

EasyEngine Install

The basic install for EasyEngine 4 is a simple bash script download and execute. Before installing, there were 23gb free on my system drive. EasyEngine installs docker and pulls several images.

wget -qO ee rt.cx/ee4 && sudo bash ee

After this install I had 19gb free on my droplet drive.

Configuring a first site and importing from Tumblr

EasyEngine makes setting up a new site a single line command. I want to set this up with LetsEncrypt SSL, so the first step is to get DNS for joshrivers.me and www.joshrivers.me pointed at the IP address of the droplet. I’m disabling Cloudflare proxying here because it currently appears that this conflicts with getting LetsEncrypt setup.

With this configured, setting up the basic site is a single command:

ee site create joshrivers.me --wp --ssl=le

After a few minutes, and entering my email address for certificate renewal notification emails, I had a functional site. I verified that redirect-to-https was configured with curl -v http://www.joshrivers.me and the web server is supplying 301 redirects to the https version of the site.

First step after creating the new site is to login with the generated admin credentials and change the display name of the account (no point in having a randomized username and printing it on every web page).

Outbound email did not work out of the box with the builtin postfix. Trying a test send from the command line gave an error: server message: 451 4.3.0 <my address>: Temporary lookup failure. Installing cli tools into the postfix container shows that name resolution is working from there. It appears there is a configuration error with the dockerized postfix where email fails when the domain matches the hosted site. The errors go away when attempting to deliver to a different domain. Frustratingly, I still don’t see any email delivery at this point. Those emails may be being blocked due to my SPF configuration for my domains. There was a useful fix described on the community forum, but I still have no mail delivery.

Ultimately, I don’t think I need transactional emails from wordpress, so I’m going to leave it alone for now. I can dig at postfix and SPF later if it seems important.

Further settings to update:

  • Site Timezone
  • Disable Comments
  • Day and Name Permalinks

Next I imported my old Tumblr Blog, which required removing the custom domain mapping in Tumblr and using an OAuth login. After importing I verified comments were disabled on all the old posts. And I deleted the “Hello World” post. I used a simpler theme for joshrivers.me than the default WP theme. It could use some work, but it is fine as a start, and at least as decent as my Tumblr theme was. Next I set up Jetpack.

Imports fron WordPress.com

For each of my additional sites, I ran through this outline:

  • DNS Reconfiguration
  • ee site create subvert.org --wp --ssl=le
  • Initial Admin Settings: https://subvert.org/admin
    • Trash sample post and pages
    • Edit default user display names
    • Change site name and tag line
    • Correct site time zone
    • Disable comments
    • Day and Name permalinks
    • Delete default plugins
  • Plugins
  • Import
    • Importing with media import seems to work seamlessly. I got an error due to an attachement from my long ago import when I got onto wordpress.com but I don’t think anything was lost there.
    • Deactivate Importer Plugin
  • Change default category

Added this to CSS to stop showing the (disabled!) comments link:

.post-comment-link {
  display: none !important;
}

Using the DB shell instructions from the EasyEngine Handbook, I was able to change the admin email address:

SELECT * FROM wp_users;
UPDATE wp_users SET user_email = "<my_address>" WHERE user_email="admin@subvert.org";
SELECT * FROM wp_options WHERE option_name="admin_email";
UPDATE wp_options set option_value = "joshrivers@me.com" WHERE option_name="admin_email";

I next tested this with the outbound email instructions. Still no mail delivery. This is bugging the heck out of me, but I also can’t think of a single reason I need to send emails from my blog server, so I should really just leave it alone, right?

Update: I also needed to do manual re-linking of images. The images exported and imported correctly, but all the image urls in the post records ended up pointing to the WordPress CDN rather than my webserver. The WordPress CDN appeared to not be serving to pages loaded from my server (referrer filtering?)