Running Postleaf on AWS with custom SSL and CDN for $11.67 a month

We’ve been looking for a blogging platform for a while. WordPress seems to be the go-to option, but I confess a bit of resistance on my part. WordPress always feels so… heavy. When I saw an article on TechCrunch about Postleaf, it piqued my interest. It promised a lightweight, elegant approach with simple inline editing. I thought I’d give it a try.

Perhaps a little background. I hate servers. Well, not really. We just have so many of them and they all require a measure of care and feeding. At Comecero, we believe strongly in “separation of duties” and as such we generally limit each server (or group of servers) to a single application or purpose that runs in isolation. This is the right approach from a security perspective, but before we add any new servers, I think a lot about ways we can accomplish what we want without spinning up yet another server.

With the more established blogging platforms, it is pretty easy to find a provider to host the blog for you. As a new player, this isn’t available for Postleaf, so self hosting was the only option. So, let’s do this in a way that provides us with the least overhead possible. We’ll use AWS Elastic Beanstalk backed with the AWS RDS service running MySQL. To add a little spice, we’ll add a custom domain name (using SSL), plus a CDN to speed up page load times for the billions (dozens?) of readers you have across the planet.

I’ll keep things simple so even the non-geeks can do this. Really. If you can use a web browser and text editor, you can do this.

Prepare the Postleaf application. Head on over to the Postleaf download page and download the code. There is one little tweak we need to make to the source code to make this work with our custom SSL. Our Elastic Beanstalk application will be proxied behind CloudFront, and Elastic Beanstalk only runs over HTTP when not using a load balancer. As such, even though clients will request our blog over HTTPS (to CloudFront), CloudFront will pass those requests to Postleaf over HTTP (because that's the only connection our Elastic Beanstalk application will accept), and the Postleaf application will believe it's running over HTTP and will therefore will build the URLs in the application as http://. This causes mixed content security warnings when the browser runs the app over HTTPS and you won't be able to install Postleaf.

This is remedied with a simple fix. There's a function within Postleaf that returns the base URL of the application; instead of returning the protocol as determined by the request to the application, I updated the code to return a "relative" protocol, so that the protocol is implied by the request.

Within the code, navigate to source> classes. Open the file Postleaf.php with a text editor. Do a search for: "public static function url()" (without quotes). When you find it, change the last line in the function from:

return "$protocol://$hostname/$path";

To this:

return "//$hostname/$path";

Save the file and close. Perhaps we'll make a pull request and push this change to the main project to save this workaround in the future. That's the joy of open source, right?

Next, we need to add all of these folders and files to a new zip file. Navigate to the root folder of the application (the folder that contains “backups”, “content”, “source”, “index.php”, etc.) Select all of the files and folders within this folder, right click on them and select Send To> Compressed (zipped) folder (see screenshot below). Name it “postleaf-rezipped.zip” and you’re done. Remember where this file is, we’ll use it later.

Create an account on AWS, if you don’t already have one. The good news is that everything we are about to do falls within the AWS “free usage” tier, which means if your AWS account is new, your cost to host Postleaf for the first year will be nothing / nada / nil / $0.

Set up Elastic Beanstalk. From the top menu, go to Services> Compute> Elastic Beanstalk. In the upper right corner, click the “Create New Environment” link. Choose a name and a description - you can enter anything, but I don’t think you can change it later. “My Blog” might be OK. Click Next.

From the next page, choose Create Web Server. From the next page under Predefined Configuration, select “PHP”. Under Environment Type, select Single Instance. Note: you can also choose “Load balancing, auto scaling”, but it will drive your costs up per month quite a bit. Click Next.

On the next page under Source, select “Upload your own”. Click Choose File and navigate to the postleaf-rezipped.zip file we created earlier. Click Next. This will take a few moments while the code uploads.

On the next page, it’s going to ask you about your environment. Give it a name (which can’t include spaces). Something like “MyBlog” or “my-blog” or whatever. Then choose an environment URL. This must be unique across the Amazon region you’re in, so you’ll probably need to choose something that is likely to be unique such as “my-company-blog” or something. Don’t worry, no one but you will need to know this URL.

On the next page, select “Create an RDS DB Instance with this environment” and “Create this environment inside a VPC”.

On the next page, select “t2.nano” as the Instance type. If you expect more than a couple dozen users on your site at the same time, you could bump this up to t2.micro, which doubles the capacity. Don’t worry, you can change this later at any time. If this is a new blog, start with the t2.nano and if it seems slow later as the traffic grows, you can increase it with a couple of clicks.

Under EC2 key pair, just leave it to “Select a key pair”. We don’t intend to ever log into this server. Enter an email address to be notified about any issues with your environment. Under Health Reporting, choose Enhanced, under Root Container Type choose General Purpose SSD. Leave the rest as default.

On the next page “Environment Tags” leave blank and just click Next.

On the next page “RDS Configuration”, enter the following values:

  • Snapshot: None
  • DB engine: mysql
  • DB engine version: (whatever is selected by default)
  • Instance class: db.t2.micro
  • Allocated storage: 5 GB
  • Username: Choose a username, perhaps “master” or “admin”. Whatever you want.
  • Password: Choose a password. Make sure you remember / record your username and password! You’ll need them later! Make sure your password is at least 8 characters or you will have problems later.
  • Retention setting: Create snapshot
  • Availability: Single availability zone

On the next page “VPC Configuration”, check the box “Associate public IP address”, check the boxes under two of the availability zones under EC2 and RDS as in the image below. You need to select at least two zones. It doesn’t matter which zones you select. Leave everything else as default.

On the next page “Permissions” leave the defaults and click Next.

On the next page “Review”, scroll down and click Launch.

It will take a few minutes for your environment to launch, including your database (maybe as much as 15-20 minutes). Let it grind away. While the environment is getting ready, let’s go set up our content delivery network and custom SSL.

Set up custom SSL and content delivery network. First, we need to get the hostname of the Elastic Beanstalk environment that holds our blog.

You can find it by navigating to Services> Compute> Elastic Beanstalk. Under “All Applications”, click on the box that has your blog application. At the top of the page, you will see a little “breadcrumb” menu that starts with “All Applications >”. Follow that to the end and you should see a URL highlighted in blue. Something like “xxxxx.us-west-2.elasticbeanstalk.com”. Copy the hostname to your clipboard.

Now, let’s go to CloudFront. From the top menu, go to Services> Storage & Content Delivery> CloudFront. Click Create Distribution. Under “Web”, select “Get Started”. Fill in the following details. Any items that are not specified in my list below should keep the default values.

  • Origin Domain Name: paste the hostname from the Elastic Beanstalk environment that we previously copied.
  • Origin Path: leave blank
  • Origin ID: Leave the default value
  • Origin SSL Protocols: Leave default values
  • Origin Protocol Policy: HTTP only
  • HTTP / HTTPS ports: Leave defaults
  • Viewer Protocol Policy: Redirect HTTP to HTTPS
  • Allowed HTTP Methods: Select “GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE”
  • Forward Headers: Select “Whitelist”, from the menu on the left, scroll down to Host, select it and click “Add”.
  • Object Caching: Select Customize and set Minimum TTL to 0, Maximum TTL to 0 and Default TTL to 0. (This effectively turns caching off which will be useful for when we are getting our blog set up, we’ll change this later when we go live to increase performance).
  • Forward Cookies: All (this is necessary so we can log into the admin portal)
  • Forward Query Strings: Yes
  • Compress Objects Automatically: Yes
  • Prices Class: All edge locations
  • Alternate Domain Names (CNAMEs). This is where you enter the custom domain you would like to use, such as “blog.mycompany.com”. We’ll only need to enter one value, even though you can enter multiple.
  • Under SSL Certificate, select “Custom SSL”. Then click Request an ACM certificate.
  • On the new page, enter the domain name for your blog that you previously entered, such as “blog.mycompany.com”. Then click Review and Request.Then click Confirm and Request. An email will be sent to the domain owner (i.e. the person that registered or controls the domain. That person might be you.). Open the email and click the link to approve the request. If you are not the person that registered the domain, ask the person who did to click the link to approve.
  • Now, go back to the page where you were previously setting up CloudFront. Click on the little “reload” button above the “Request an ACM certificate” button. The SSL certificate you approved should now appear in the list. Select it.
  • Under Custom SSL Client Support, select “Only Clients that Support Server Name Indication”. Most browsers these days support SNI. Some old browsers don’t, if those are important to you, you can select the other options for $600 a month (ouch).
  • Under Default Root Object, enter index.php
  • Select Create Distribution

It will take about 15 minutes for the distribution to be created. In the meantime, you can go and set up your DNS.

Set up DNS. We now need to change our DNS to direct users that request “blog.mycompany.com” (or whatever you chose as your blog hostname) to load our blog from our Elastic Beanstalk application. This is done by setting up a DNS CNAME record, and is done where your DNS is hosted, which is probably the same place you registered your domain. You can think of a CNAME kind of like call forwarding on a phone. What a CNAME entry does is says “whenever someone enters ‘blog.mycompany.com’, show them the content that is hosted at something-else.somewhere-else.com’. Since we want to use our own domain and not the domain assigned by Elastic Beanstalk, the CNAME will help us.

Here’s an article that will help you with this, if you are unfamiliar with how to set up a CNAME. The CNAME you create should point “blog.mycompany.com” to the hostname of your CloudFront distribution. To get the value, in AWS, go to Services> Storage & Content Delivery> CloudFront. In the list of distributions, find the one you created and look for the column “Domain Name”. This is the value you want to use for your CNAME entry.

Create your database. Navigate to Services> Database> RDS. On the left, click on Instances. Click on your blog database instance from the list and under Instance Actions, select Modify. Scroll down to Network and Security and under Publicly Accessible, select Yes. Under Backup, under Backup Retention Period select 30. Scroll down and select the box “Apply Immediately” and then click Continue.Then click Modify DB Instance.

Now go to Services> EC2. From the menu on the left, select Security Groups. In the list, you will see a security group that most likely has a blank name, but the description will be something like “Security group for RDS DB Security Group…”. Click on it. Down below, there will be an tab “Inbound Rules”. Click it, and then click Edit. Click Add Rule, under Type scroll to MySQL/Aurora, and in Source select My IP. Click Save.

We’re going to need to download a little program to do our next task. After we’re done with this task, you can uninstall the program. Download HeidiSQL.When complete, install it and then run it. When it launches, click New in the lower right corner. In the Settings tab, fill in the following:

  • Network type: MySQL (TCP/IP)
  • Hostname / IP: The hostname of your MySQL server. In AWS, Go to Services> Database> RDS. Click on Instances in the left menu. Click on your database instance from the list. Near the top you’ll see a value next to “Endpoint”. Copy everything before “:3306”. For example: xxxxxx.yyyyyyyyyyy.us-west-2.rds.amazonaws.com. This is the value you’ll need to paste into HeidiSQL as the Hostname / IP.
  • User: The database username you entered when you created your database.
  • Password: The database password you entered when you created your database.
  • Port: 3306

Click Open. It will ask you if you want to save your session, you can select yes or no. It will then launch a screen that is connected to your database. Right click on the connection in the left of the new screen and select Create New> Database. Under Name, enter “blog” (without quotes). Under Collation, select utf8_general_ci. Click OK. (See screenshots below). The database will be created. You can now close the program.

Install Postleaf. OK, we’re almost done. Before your proceed, make sure your CloudFront distribution is ready (Services> Storage & Content Delivery> CloudFront). Under “Status” you should see “Deployed”. If it still says “In Progress”, wait until it’s done. Check that your Elastic Beanstalk application is ready. Go to Services> Compute> Elastic Beanstalk. Your application box should be green (not gray or red). If all is ready, it’s time to install Postleaf.

Point your browser to “blog.mycompany.com” (meaning, the domain name you chose for your blog). You will be directed to the Postleaf installation page. Fill in the details as outlined below:

  • Name: Enter your name, this will be used for display. You can change it later.
  • Email: Your email address
  • Pick a username and password. Remember / record them for later use.
  • Host, enter the database hostname (In AWS, Go to Services> Database> RDS. Click on Instances in the left menu. Click on your database instance from the list. Near the top you’ll see a value next to “Endpoint”. Copy everything before “:3306”. For example: xxxxxx.yyyyyyyy.us-west-2.rds.amazonaws.com.)
  • Port: 3306
  • User: The database username you entered when you created your database.
  • Password: The database password you entered when you created your database.
  • Database Name: blog
  • Table Prefix: leave the default value.

Click Install and Postleaf will finish installing and will take you to the admin page. You’re all set! Happy blogging!

How to get the price of your hosted blog with custom SSL to $11.67 per month. To do this, you'll need to pay your hosting on an annual basis using AWS "Reserved Instances" for your EC2 and RDS (database) instances. With reserved instances, you get a discount for committing to and paying for a year up front. If you don't use reserved instances, you'll pay about $17.16 a month.

Footnote: The one caveat to this approach is that the assets you upload for your blog (such as images) are stored within the same directory as the application itself. That means if I deploy a new version of Postleaf to my Elastic Beanstalk application, I will override / delete all my image uploads, which would not be a lot of fun. Perhaps in time Postleaf will support uploads being saved to and served from S3. In the meantime, when we update Postleaf, we'll have to do it manually.

Trevor Black
Trevor Black

Over 17 years ecommerce experience. Grew self funded startup company to profitability and sale. Passion for new vertical markets and new business opportunities.