Basic functionality checklist
- CloudFormation template for IAM roles and buckets
- Diagnostic/debug functionality for functions
- Figure out a good place to store configuration
- Download site sources to lambda instance
- Run hugo
- upload output to configured bucket
Advanced functionality checklist
- CloudFormation template to set up cross-domain IAM auth
- CloudFormation conditions for users who don't want to use Route53
- in-browser markdown editor
- SNS cancellation for in-progress jobs to reduce redundant hugo runs
- Download this repo and run the CloudFormation template
- Open the Lambda function in the console and add an SNS event handler from the SiteChangeTopic queue
- Upload your site's source to the input.your.site bucket
- Generated site is synced to the your.site bucket, and is served by S3 static hosting
So why go to all this trouble? Because running a CMS like Wordpress, Drupal, and others gets expensive for infrequently edited sites. You pay 24/7 to run all the code to let you edit your site, when that code only runs when you update your blog. Static site generators are cheaper to run (just toss the files somewhere) but if you're not at the computer you normally use to edit and publish content, you may not have all your tools.
hugo-lambda solves this by adding a JS editor to your hugo site, which uploads raw markdown to S3 where lambda regenerates your site. All the niceties of a CMS, but without running a server.
To start, you'll need to have GNU
- Clone this repo
git clone https://github.com/ryansb/hugo-lambda
- Replace "input.rsb.io" with your input bucket in
- Replace "rsb.io" with your own domain in
template.ymlThis must be the root of a Route53 domain you control, and an available bucket name.
- Create the function and roles by running
- Upload all your content to input.yourdomain.com
If you'd just like to try it out, the "demo-site" directory in this repository
contains a hugo site with a single post that will work. The theme is
liquorice, distributed under the terms
of the MIT license. To use it, upload the full contents of
demo-site to the
input.yourdomain.com S3 bucket.
How it works
Lambda is a service that lets you define a node.js function and have it run whenever a specific trigger happens. These triggers can be new objects in S3, events in a Kinesis stream, or DynamoDB table updates. Those functions can access other buckets, download files, or do just about anything else. The kicker is you only pay for the time your function is running. Instead of paying for an instance 24/7 to react to events, you only pay for the time spent actually reacting. This makes lambda incredibly cheap for cases where you have an infrequent event that requires some action to happen.
There are two buckets where content resides. The first is
input.yoursite.com, which contains all the information hugo needs to generate
your site. The second is the website bucket that holds the finished site and
serves it to the world.
When working on the functions, you can manually zip the code (with dependencies) and upload it to the Lambda console.
Ok, that CFN template is pretty large/gross. To make it easier to manage and edit, I've moved to editing it in YAML and generating the JSON version to send to AWS. If you edit the YAML, you can regenerate the JSON template one of two ways.
- Make sure the
PyYAMLmodule is installed and run
- Use a site like yamltojson.com
To use it, you'll need to provide the base domain of your site. For example, mine is rsb.io. This will determine the name of your buckets and will make the correct Route53 aliases to make your site accessible.
Right now, the following resources are created by the template
- 3 buckets:
- 2 domain records: one for your apex and one for
www.ROOT, which redirects to
- ExecRole IAM Role. The ExecRole is the role that the lambda functions take on when they execute. Thi role gets access to the S3 buckets to upload and download content.
- SiteChangeTopic SNS queue, because CloudFormation can't set up bucket notifications to Lambda directly.
Troubleshooting - Oops, my stack failed!
The most common problem you'll hit is probably not having a Route53 record set for your domain. The stack won't create one, so it'll fail instead. To get around this you can just delete the "SiteDNS" resource. Nothing depends on it, so no worries.
The next one will probably be IAM problems. If this happens, make sure you have the permissions to create/edit IAM roles. Typically if you aren't an administrator on your account (or are using a scoped-down IAM role yourself), you won't have the permissions to do this.
If there are problems with the function itself, you can use the awslogs tool to view the logs from your function group.
Questions, suggestions, bug reports, and contributions are welcome as pull requests or issues on this repo. Please see the Contributor code of conduct for community guidelines.
This project is released under the GNU Affero General Public License, see LICENSE.txt for the full text of the license.