A mostly static site with S3, Lambda, Jekyll, and Flask

TLDR

Creating a simple mostly static website on AWS is surprisingly complicated, but great once you get it all knitted together. I’ve created a bootstrap repo that could get you up and running in an hour or two.

Here is an example website

Here is a Github repo to get you started

Background

I needed to create a website for the new product I’m building. I tried a static site before, but it turns out that you almost always need at least a little back-end functionality — even if it is just for processing a web form. So static is out. I’ll use my goto — Flask.

But the Flask installation on my everything server is python 2.7. And it turns out the Apache mod_wsgi can only run ONE python distribution, and I don’t want to do any more 2.7 work. But to get 3.6 to work, I’d have to install another server (like gunicorn) and then figure out how to link that up to my apache. Or install another linux server, and deal with either manually installing stuff, or go through the hassle of updating all my build scripts (ansible). From experience, that would take a while.

The alternative was something I wanted to try: a static site on S3, with a backend server on Lambda. I figured this would also be a lot of work, but not much more than my other options. I decided to go for it.

So now here it is: http://FileSimple.info

GitHub Repo

Here is a link to a “Hello, World” repo that could help you get started. Note that it is neither a push-button, best practices scaffolding maker like Yeoman nor a super clean and minimal “Hello, World.” Instead this is a slightly cleaned up version of my own simple site, with all the bad or idiosyncratic decisions I made along the way embedded.

Architecture

Super simple in requirements, but LOTS of parts in implementation:

Static pages

Backend

Backend functionality

  • Save feedback in DynamoDB

Deployment

Other AWS stuff

That’s a lot for a tiny site! But my hope is that given what I learned, doing it next time will be MUCH easier. And now I don’t have to spin up new servers (and worry about security). Also, once you have the basic infrastructure working front-to-back, implementing a new service (like email, messaging, database, etc.) is pretty easy — easier than getting it running on your own linux server.

Disclaimer

I am a self-taught programmer. The amount I know is massively outweighed by the amount I don’t know. I’m sure my ignorance comes shining through. But my goal was to get my first AWS site up without taking too much time. The next time I’ll dig deeper and start filling in my ignorance.

The Hardest Parts

  • Permissions — ugh! I tried to manage the permissions myself, which was difficult. In my Hello, Lambda repo I let Zappa manage it, which is a lot easier (use the `extra_permissions` setting in zappa_settings.json). Unfortunately Zappa is quite permissive with its default permissions, which is a bit troubling.

Development Notes

  • Python — I like python so I use it wherever possible. If you prefer JS, your stack will differ.

Helpful Feature #1: Local & Remote Interactive Testing

After a bunch of iterations I created an interactive test page that works locally or remotely. It was very helpful in debugging. Example

Convenience Features

  • Static pages hosted locally with LiveReload (`jekyll serve`) or on S3

Helpful Feature #2: Rate Limiter

One thing that concerns me about AWS is it’s unbounded liability. If someone hacks or slams my Digital Ocean droplet, I have some downtime, but my cost is still $10/month. If someone attacks my AWS installation, who knows how much that could cost me?

I’m no expert, but start with:

  1. Be CAREFUL with your AWS credentials. Make sure they are NEVER in source control. (Add `.aws/credentials` to your .gitignore)

Still, I felt nervous. In order to make progress with my projects, I accept a lot of ignorance. That’s usually ok, but not if it could wind me up with a $20,000 AWS bill. I’m assuming that’s pretty unlikely, but I wanted to make that discomfort go away.

My top-of-mind security risk is that someone would maliciously slam my form API, just to hurt me. So I created a little rate limiter decorator to protect my DynamoDB and SES calls.

The code is here: limiter.py

Use it this way:

Conclusion

I’m glad I did it. I learned a lot and I can see this as a good way forward.

I’m eager to try site #2!