KISS
Before we get too deep into the details, I'd like to detail the driving principle behind being able to deploy a website for this cheap. And that is the KISS Principal. KISS stands for "keep it simple, stupid" or "keep it stupid simple" depending on who you ask. Surprisingly, the saying draws its roots from the U.S. Navy, but is applicable to any complicated task. It's particularly applicable while developing software. The millions of ways to develop and deploy applications can be overwhelming when starting out. There's different frameworks, Server-side rendering vs Client-side rendering, different hosting platforms. The amount of options you have is seemingly endless. At the end of the day, if you take your time to honestly exam what you're goals are for a project, and find the smallest tools you can to complete those goals, it often leads to the best outcome both for the creator and for the consumer.
Project Structure
To kick things off, let's get the mile-high view of how the project is built. For starters, the core of my application is React. However, you can easily do this with just plain-old HTML, CSS & JS. Next, we'll go over the AWS side of things. If you're unfamiliar, Amazon Web Services is a cloud computing platform built and maintained by Amazon. It has an extremely wide-range of services, however today we're going to focus in on two. S3 or Amazon's Simple Storage Service is a data-storage service. It's a staple of AWS and where we're going to store our static files for others to access. Secondly, Cloudfront is Amazon's Content Delivery Network. The purpose of CDNs' is to maximize availability to consumers, regardless of geographic location. So even though our site will live in a specific region, we can ensure that everyone has a consistent experience while accessing our product.
Continuous Integration (and it's sibling Continuous Deployment) is the act of automating the process of delivering the production version of an application. CircleCI is a product that provides integrations for deploying applications to common targets. We'll use it to wire our project to AWS.
Getting Started
Project
Let's jump in.
$: npm i -g create-react-app
$: create-react-app demo
This will create a React boiler plate project that we can use to setup our deployment process. Next let's build the project locally, to ensure we don't fail our longer process later.
$: npm run build
Once this completes successfully, let's add a repository. If you're new to Github, you can sign up here and follow this guide for how to create a repository with them. Once you've create a repository lets setup our local repo to work with the remote one we've created in Github. As well as add our first commit. If you're unfamiliar with Git or the Git CLI, I have a short tutorial series that covers the basics
Article No Longer Available
$: git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPO.git
$: git add .
$: git commit -m "Inital Commit"
$: git push -u origin master
AWS
S3
Now we'll need to setup our AWS architecture before we go too much further with the project itself. If you don't already have an AWS account, you can follow their guide here. Once you've done that head into the console and search for "S3". In the S3 dashboard, you'll be greeted by the sight of your accounts buckets in a table. You'll see a series of buttons to the top-right. Select "Create Bucket" on the far right.
You'll have a form presented to you for creating an S3 Bucket. Buckets in S3 are similar to top-level directories in a file structure. How they are configured determines how the objects inside of them behave. Simply, they are a location for you to store data and configure how the data is accessed. Here you'll want to enter a name that is easy to relate to your project and uncheck the "Block all public access" button. It warns you that this is dangerous, because when you select this setting, any data you put in the bucket can be accessed by anyone. For our use-case, this is required. We're going to be hosting a website for everyone to have access to. After you've created the bucket, select it from your table and then select the "Properties" tab.
In here, we need to configure the Bucket to be a web host. Select "Static Website Hosting" from the first row of buttons. In here you'll enter the index and error HTML file names. And that's it for S3!! Halfway through the architecture setup, lets now turn to Cloudfront.
CloudFront
If you go back to the AWS console, and search for "Cloudfront" instead of "S3" you'll be redirected to CloudFront. In the CloudFront dashboard, select "Create Distribution". Where you'll be greeted by two buttons. One underneath a section titled "Web" and another under "RTMP". Selected the button underneath "Web". You'll be greeted by a long form with a lot of complicated-sounding options. Lucky for us, the only thing we have to do is insert the bucket's site URL, into the "Origin Domain Name" field. After that you can leave everything as default and select "Create Distribution" at the very bottom-right of the page. Congrats!! That's all the scary stuff down. Finally let's connect the project we made earlier with the architecture that we finished setting up.
CircleCI
First, you'll need to create a CircleCI account here and link it with your github account. Then, head back into your create-react-app project and add a root level directory named, ".circleci". In it create a file titled "config.yml". Paste below, into the file, replacing BUCKET_NAME with the name of the bucket you created earlier.
version: 2.1
orbs:
node: circleci/node@1.1.6
aws-s3: circleci/aws-s3@1.0.11
jobs:
build-and-test:
executor:
name: node/default
working_directory: ~/repo
steps:
- checkout
- node/with-cache:
steps:
- run: npm install
- run: npm test
- run: npm run build
- aws-s3/sync:
from: build
to: 's3://YOUR_BUCKET'
overwrite: true
arguments: |
--acl public-read \
--cache-control "max-age=86400"
workflows:
build-and-test:
jobs:
- build-and-test:
filters:
branches:
only: master
One other thing to note, is the "filters" object at the very bottom. This is saying that, whenever their is a commit to the master branch, I want to build and deploy the website. Once you push this file to the remote, you should no longer commit directly to master, otherwise you'll trigger a deployment with every commit! This would not only lead to extra charges on your AWS account, but would most likely lead to a poor experience for your user.
In the CircleCI Dashboard, select the "Projects" tab from the sidebar. You should see a list of all the repositories in your Github account. To the right of each repository, you'll see a "Set Up Project" button. Select the button for the repository that houses the create-react-app project. Inside, you'll be greeted by a text-editor with a sample node configuration. Above that you will see this ribbon. Select "Add Manually". At this point, CircleCI should be running and within a few minutes, you should have your first deployment!!
Conclusion
You now have your website hosted on AWS!! How's it feel? I know at first AWS can be super overwhelming, but like most things once you spend time in it, getting things done becomes second nature. If you would like to see how to setup a custom domain, had any issues following this guide, or have questions in general, you can comment below or reach out to me on twitter.
Find me on Twitter | LinkedIn
Sponsor me on Github
Like the article? Buy me a coffee!
Top comments (14)
You can do this without any cost by using vercel !
Right, but for $1/mo you basically force yourself to learn AWS. Which is useful in a lot of other ways, you know?
I think it's contextual. If you have either people that know AWS or can pay for those people, go the AWS route. If you don't, services like Vercel or digital ocean are great to get products to market. Then you can get people/money.
yes agree with that :D
I checked Vercel and it seems like that could be useful too...
If I remember correctly, Vercel is built mainly on AWS. You absolutely can get a similar result with it, but after a point they will charge you at a higher rate than if you did it yourself. I have nothing against Vercel or people using it, but being able to build from the source will always be cheaper in the long run. The cost is upfront with having to learn what I detailed in this post.
Interesting! Will be nice to have a Part II with comparisons.
So can this setup have WordPress templates with Plesk or cPanel management?
How is your $1 a month site in terms of traffic and applications, compared with an equivalent Lightsail install that looks the cheapest to me in the Big Cloud space?
Lightsail is more similar to traditional hosting services, in that it is spinning up a whole network environment to host your application. The deployment I detailed above is serverless. So wordpress and tools like cPanel are out of the picture entirely. In terms of handling traffic, the metrics are different. Performance isn't limited to a single machine or location, so a lot of the normal bottle-necks that come with single-machine hosting are removed. The tradeoff is that it can handle all those requests, and AWS is more than happy to charge you for doing that. So you do have to be careful that you bundle your application fairly small, and possibly limit the geographic regions that can access your distribution.
You can also use a tool like Terraform to create the Cloudfront and S3 infrastructure for you. Then I think that this whole solution would be repeatable after that (with a bit of templating added)
That is a great idea!! Personally I have more experience with CloudFormation than terraform, but I might have to put out a template for the project either way 🤔
Simple is awesome, thanks Dakota.
With gitHub Pages, you can host your website for free.
Woah TIL about CloudFront. I thought AWS Lightsail was the cheapest option. Thanks a lot and will definitely try this in the future 🦄
Lightsail with just a virtual machine is cheap. Lightsail with anything else is not, compared to rolling those services yourself. Glad to hear that you found this helpful :)