Static websites have exploded in popularity over the past few years. What is it that people like so much about static site generators?
- Pretty darn fast - Static websites that keep an eye on asset sizes are generally pretty fast around the world when deployed behind a CDN, but only if those caches are warmed up. More on that later.
the stupid content tracker. Dynamic systems, like Wordpress, use databases to store content, which requires versioning schemes that are implemented at the application level.
Despite these dreamy characteristics of running static websites in production, it does come with a few trade-offs, especially if there's a few dynamic things that are needed, like publishing content with a future publish date.
Let's look at a few approaches for how one might accomplish such a feat so we can better understand these trade-offs.
Problem: How Do I Publish Future Content?
A common use case when dealing with content, like a blog, is scheduling a post to be published in the future. It's a simple task for a dynamic content management system, but for static site generators, it requires some work. Here's a few different approaches to the problem.
The Static Way: Use
cron to Build and Publish the Site
One way to publish future content with a static website is to schedule an hourly or daily task in
cron that builds all the HTML pages in the website and uploads them to a production environment. This works fine for a small website that's not updated frequently, but it falls over for larger websites that need to publish content at more fine-grained intervals. Imagine wiring up a cronjob that runs every minute to compile and upload a website with a few thousand pages?
The Database Way: Run a CMS Like Refinery or Wordpress
On the other end of the spectrum there's the Content Management System. The most popular CMS in the world is WordPress. For Rails, the most popular CMS is Refinery according to the Ruby Toolbox.
This approach to managing content comes with a lot of complexity. For starters, these approaches require databases. Databases are hard to sync between production, staging, and development environments. Databases break. It's difficult to version and merge conflicting data in the database if people are editing the same content. Usually some sort of caching layer needs to be built above the content generated by the database. It's a lot of extra complexity that might not be worth it.
The Middle Road: Deploy a Semi-Static Website to Fly
Semi-static websites, also known as server-side rendered (SSR) sites, run a small server in production that renders content per request. For a blog, that means the server could check a
publish_at frontmatter key on a markdown file like this:
--- title: My First Blog Post publish_at: January 1, 2042 --- Hello! I hope 2042 is a great year.
And quickly figure out whether or not to display the post based on the server's current time.
How to Build and Deploy a Semi-static Website to Fly
It's pretty quick! Here's how to do it:
- Clone the repo https://github.com/sitepress/standalone-starter.
git clone firstname.lastname@example.org:sitepress/standalone-starter.git
- Install the Fly CLI and signup for an account.
- Now we're going to run a command that provisions the app and deploys it:
fly launch --copy-config --dockerfile Dockerfile --now
- Once that finishes, run
fly openand you should see a website that looks like this:
Hooray! You've published your first semi-static website. It's running a webrick server that renders everything per-request.
How Fast Is the Website We Just Deployed?
Now let's see what things look like for people on the other side of the world by running
fly curl, a nifty little tool that loads your website from various Fly outposts.
fly curl https://$YOUR_SITE_NAME.fly.dev
REGION STATUS DNS CONNECT TLS TTFB TOTAL ams 200 0.7ms 0.8ms 29ms 193.5ms 195.1ms cdg 200 0.7ms 0.9ms 29.1ms 179.5ms 181.1ms dfw 200 0.7ms 0.9ms 29.5ms 71.3ms 73.5ms ewr 200 0.5ms 0.7ms 25.1ms 90.6ms 91.8ms fra 200 0.6ms 0.8ms 30.5ms 204.7ms 205.9ms hkg 200 0.6ms 0.7ms 21.7ms 186.9ms 188.5ms lax 200 0.5ms 0.7ms 21.9ms 50.5ms 51.2ms lhr 200 0.8ms 1ms 29.2ms 174.3ms 175.9ms mia 200 0.8ms 1.1ms 33ms 116.4ms 160.6ms nrt 200 0.4ms 0.5ms 17.4ms 135.8ms 136.5ms ord 200 0.6ms 0.8ms 22.8ms 93.3ms 95.2ms scl 200 1.1ms 1.5ms 48.3ms 211.3ms 213.3ms sea 200 0.4ms 0.5ms 16.3ms 57.4ms 57.9ms sin 200 0.5ms 0.6ms 17.5ms 204.6ms 205.2ms sjc 200 3.3ms 3.4ms 34.9ms 46.1ms 46.4ms syd 200 0.7ms 0.9ms 24.9ms 192.9ms 193.6ms yyz 200 0.6ms 0.8ms 31.6ms 99ms 99.9ms
The time-to-first-byte (TTFB) times on the other side of the world probably aren't all that great, which means people trying to read your website are sitting there waiting. TTFB is the amount of time people have to wait after typing
your website.com into their browser and receiving the first bytes of HTML.
Provision and Deploy to Servers Around the World
Let's fix that problem by deploying this website closer to them, without a CDN, by telling Fly we're cool running our site in these regions:
fly regions set sin fra ord
Region Pool: fra ord sin Backup Region:
This command tells Fly the parts of the world you want to deploy the website, but the servers aren't running there yet. Let's spin them up.
fly scale count 3 --max-per-region=1
Fly will scale up your semi-static site in each of these regions so they're ready to respond to requests as they come in.
Let's see how those regions scaled up.
App Name = $YOUR_SITE_NAME Owner = personal Version = 2 Status = running Hostname = $YOUR_SITE_NAME.fly.dev Platform = nomad Instances ID PROCESS VERSION REGION DESIRED STATUS HEALTH CHECKS RESTARTS CREATED 394677de app 2 ord run running 1 total, 1 passing 0 1m6s ago 4de70730 app 2 sin run running 1 total, 1 passing 0 1m6s ago 8f6646aa app 2 fra run running 1 total, 1 passing 0 2m21s ago
Now How Fast Is Our Website?
fly curl again and see what happened to the latency:
fly curl https://$YOUR_SITE_NAME.fly.dev
REGION STATUS DNS CONNECT TLS TTFB TOTAL ams 200 0.7ms 1ms 29ms 37.7ms 39.4ms cdg 200 0.9ms 1.1ms 22.9ms 44.5ms 45.9ms dfw 200 0.8ms 1.2ms 25.9ms 68.1ms 69.3ms ewr 200 6.8ms 7.1ms 85.5ms 70.3ms 71.3ms fra 200 0.8ms 1.1ms 26.9ms 33ms 34.4ms hkg 200 0.9ms 1.1ms 22.7ms 70ms 71.7ms lax 200 0.5ms 0.7ms 21.7ms 86.4ms 87.1ms lhr 200 0.8ms 1.1ms 24.5ms 36.4ms 38ms mia 200 0.8ms 1.2ms 25.3ms 69ms 70.8ms nrt 200 0.4ms 0.6ms 18.1ms 94.1ms 94.6ms ord 200 0.4ms 0.6ms 21.8ms 51.9ms 53ms scl 200 1.1ms 1.4ms 39.8ms 168.1ms 170.3ms sea 200 0.4ms 0.5ms 16.1ms 83.2ms 91.6ms sin 200 0.4ms 0.5ms 17.4ms 26.3ms 26.9ms sjc 200 0.4ms 0.5ms 28.1ms 83.6ms 83.7ms syd 200 0.9ms 1.1ms 23.4ms 113.7ms 115.1ms yyz 200 1ms 1.5ms 41.8ms 60.8ms 62.3ms
Much better! The TTFB time decreased, which means people who are reading the website see the content instantly (under 250ms) as far as they're concerned.
Fly, Content Distribution Networks, Your Customers, and You
With Fly, it is possible to cut out the middleman CDN and simply run content servers closer to your customers. CDNs are great, but they do add latency to your application for a few reasons:
- A CDN with a cold cache has to fetch the content from the origin. This adds latency to the initial request.
- When the cache from a CDN expires, it has to check the origin for a fresh resource which again, adds latency to the request. Some CDNs can be configured to serve up the stale content while requesting the new stuff from the origin, but nobody wants something that's stale. If people like stale stuff bakeries wouldn't mark down day-old-bread.
For static websites, this isn't a huge deal, but for mixed websites where all application requests go through the CDN and the cache times are low like a blog or news website, its at least an extra hop that simply isn't necessary when running servers close to your users.
There's Lots of Reasons to Deploy Semi-static Websites
Here's a few that you could try for your own projects:
- Always up-to-date project README files - Lots of open source projects have a website that repeat content from the
README.mdfile at the root of their project. With a semi-static site, its easy to fire off an HTTP request for an open-source projects latest
README.mdfile and render it within the project website. The starter app shows how a Github README could be rendered on a project page.
- Localization - A semi-static site could localize content depending on the
FLY_REGIONor users IP address.
- Treat Your Content As Data - Imagine a world where you could get a list of relevant help articles to your customers from within your application? It's possible with Sitepress via a line of code that looks like
HelpPages.tags(:login, :security, :account_management). Treating content as data opens up a lot of interesting use cases for tightly integrating content with your app, which can make for a great experience for your customers.
- Community Websites - Git is the ultimate content management system for communities, especially when it's backed with workflows like "Open a PR to edit content". It's how Fly's docs are managed. Be sure to include an "Edit with Github" link on the page that opens the content up in Github's edit view so people can make contributions for quick edits, like typos.
- Run it in your Rails Apps - If you're a small team or solo developer, Sitepress can be embedded in your Rails apps and integrate directly with your routes files. If the Rails application has a database, you'll want to read about how to run ordinary rails apps globally.