We live in interesting times. The cloud offerings from Amazon, Google and Microsoft have led to a situation where world-class infrastructure is available to startups and solopreneurs for very cheap, and most of the times, they can even get started for free.

While standing up a business, it is critical to have a lean operation with a performant, stable and scalable product. This reduces user churn and increases retention. It is important that the online service you provide has high uptime without costing too much and reducing your runway.

In this post, I outline an AWS based architecture which you can most likely run for free during at least the first 12 months and then for a few dollars per month while you build traction for your product. The cost really depends on the amount of traffic you serve and how your application is architected. But, more traffic is hopefully more revenue, which is a good problem to have!

I also discuss estimating the monthly bill based on some assumptions mentioned in the following section. Hopefully, it will help you evaluate various scenarios specific to your product or service for a more predictable bill from your cloud provider.

Assumptions

While calculating the price, I assume the website or service gets about 1,000 user sessions a day and in every session, the user makes 50 API requests and 50 requests to static assets (Images, HTML, CSS and Javascript files). The average size of static assets is 50kb, execution time of application code is less than or equal to 100ms per request and memory usage is less than or equal to 128MB per request. The average size of a read from DynamoDB is 20kb and write is 20KB per second. The total size of stored assets on S3 is 15GB and the traffic is split as approximately 50% from US & Canada and 50% from Europe

The pricing I discuss in this post is for AWS US-West (Oregon) region. The prices for other regions may vary.

Architecture

Pieces

API Gateway Lambda DynamoDB S3 CloudFront DNS & Route 53

API Gateway

API Gateway is the service to create APIs to access AWS resources. In this setup, API Gateway is used to invoke a function in AWS Lambda. This service forwards the request parameters to AWS Lambda which then, executes the application code and returns a response that is forwarded to the user.

Usage for this service is calculated as a mix of the number of API calls per month and the amount of data transferred.

Product link: https://aws.amazon.com/api-gateway/

Pricing

Free Tier - 12 Months

This service offers 1 million API calls/month in its Free Tier, which means it can support 666 user sessions per day in the Free Tier assuming each user makes about 50 API calls in a session. The Free Tier for this service expires after 12 months.

After Free Tier

After the 12 month Free Tier expires, AWS charges $3.50 per million calls and $0.09/GB for first 10 TB. If you expect 1,000 user sessions a day making 50 requests with an average data transfer of 50KB, it adds up to 1.5 Million API calls and 75GB of data transfer per month. Which should cost $12 per month ( 1.5M*$3.50/M + 75GB*$0.09/GB = $5.25 + $6.75 ) .

More on pricing: https://aws.amazon.com/api-gateway/pricing/

AWS Lambda

Lambda is the serverless offering of AWS where you can implement functions that are invoked by the API Gateway. The code can be edited inline or uploaded to S3. The usage for this service is defined as a mix of 2 things: number of requests and duration.

The number of requests is pretty straightforward. Where it can get interesting is the duration which is measured in GB-seconds. To calculate GB-seconds for your application, you would multiply the memory usage to the time taken by a request to execute. This is discussed more in the pricing section.

AWS Lambda also enforces certain memory and time limits. This is particularly important while calculating the duration. Minimum memory usage of any function is set at 128 MB and the maximum is 3008 MB (with 64 MB increments). If the maximum memory use is exceeded, function invocation is terminated. Similarly, it also measures time in increments of 100ms with a minimum of 100ms and maximum of 300s (5 minutes) in increments of 100ms (more on Lambda limits: https://docs.aws.amazon.com/lambda/latest/dg/limits.html).

Product link: https://aws.amazon.com/lambda

Pricing

Free Tier

This service is free for 1M request/month, and 400,000 GB-seconds/month of compute time. This Free Tier does not expire after 12 months.

For instance, if your request executes in 60ms consuming 100MB, AWS lambda will bill you for 100ms and 128MB memory, you would be able to serve 31,250,000 requests ( 400,000 GB-seconds/100ms*128MB ). 1,000 users sessions with 50 requests a day translates to 1,500,000 requests a month which is well below the Free Tier limits.

After Free Tier

Once your application makes 1M requests AWS charges $0.0000002 per request and after exceeding 400,000 GB-seconds, $0.00001667 for every GB-Second used thereafter. For instance, if your request executes in 100ms and consumes 128MB, each request takes in 0.0128 GB-Second. Cost per request comes out to be $0.000000413376 ( $0.0000002 + 0.0128*$0.00001667 ) translating to $0.413376/1M requests.

More on pricing: https://aws.amazon.com/lambda/pricing/

DynamoDB

Dynamo DB is a NoSQL database offered as a service by AWS. DynamoDB defines usage in terms of read and write capacity. 1 write capacity means 1 write/second in 1 KB block and 1 read capacity means 1 strongly consistent read per second, or 2 eventually consistent reads per second in 4KB blocks. This means if you read 1 KB in 4 requests it counts as 4 reads compared to reading a bigger chunk, say 3KB in one request, which is considered 1 read.

Data transfer within the same region to other AWS services is free. Reads and writes are averaged in 5 min intervals to calculate the per second values. Estimating the price for this service can get a little complicated. To get a clear picture, try to estimate the amount of the indexed data you will store and the access patterns - reads and writes per second.

Product link: https://aws.amazon.com/dynamodb/

Pricing

Free Tier

In the Free Tier, AWS DynamoDB offers 25 GB of indexed storage, 25 units of write capacity and 25 units of read capacity. Data transfer is free within the same region. This Free Tier does not expire after 12 months.

After Free Tier

Once you exceed the Free Tier, the following is the pricing:

$0.00065 WCU per hour $0.00013 RCU per hour $0.25 per GB-month after 25GB

If you are storing 20KB user data for every user, you can store data for 1.25 million users under the Free Tier limits and $0.000025 per user thereafter.

If you are reading all the 20KB of data for a user per second, strongly consistent, that is 20KB per second. It translates to 5 Read Capacity ( 20KB/4KB ), which is covered by the Free Tier and you still have 20 more read capacity to spare. For every additional user read per second past the Free Tier, it will cost $0.468 per month ( 5RCU * (30*24)hours * $0.00013 RCU/hour )

Similarly, for writes, 20KB write per second translates to 20 write capacity, which is also covered in Free Tier with 5 write capacity to spare. After the Free Tier, you can expect a full write of 20KB per second to cost $9.36 per month ( 20 RCU * (30*24) hours * 0.00065 WCU/hour )

Most of the applications are read heavy, which means that they do more reads than writes, in the above example we assumed 1 write for every read.

More on pricing: https://aws.amazon.com/dynamodb/pricing/

S3

AWS S3 can be used to store static content like HTML, CSS, JS files or code for lambda function. The usage for this service is defined in terms of Storage used in GB, number of requests and data transferred in GB/month.

Product link: https://aws.amazon.com/s3/

Pricing

Free Tier - 12 months

Free Tier for S3 includes 5 GB of Amazon S3 storage in the Standard Storage class, 20,000 Get Requests, 2,000 Put Requests, and 15 GB of data transfer out each month. This Free Tier expires after 12 months. The data transfer to CloudFront is free. So, if you use CloudFront as a CDN, as proposed in this architecture, your S3 costs should be $0.

After Free Tier

Once the Free Tier limits are exceeded or the 12 month trial period is over, the costs are $0.023 per GB for the first 50TB. PUT, COPY, POST, or LIST Requests cost $0.005 per 1,000 requests and GET, SELECT and all other Requests cost $0.0004 per 1,000 requests. Assuming a 15GB storage, the storage cost should be $0.345 ( 15GB * $0.023/GB ).

For data transfer, the transfer up to 1 GB/month to the internet is free. After that, its $0.09 per GB up to 1TB/Month. But, the transfer to AWS CloudFront is completely free without any limits. So, if you serve the static assets with CloudFront as CDN in front of S3, you do not pay anything for data transfer to CloudFront. The CloudFront pricing is discussed in the next section.

More on pricing: https://aws.amazon.com/s3/pricing

CloudFront

CloudFront is Amazon's Content Distribution Network. This is meant to deliver static assets to users substantially faster compared to directly accessing from S3. The usage for this service is defined in terms of data transferred out and the number of requests served.

The pricing for this service varies with regions. This is elaborated more in the following section on pricing.

Product link: https://aws.amazon.com/CloudFront/

Pricing

Free Tier - 12 Months.

In the Free Tier, AWS offers 50 GB Data Transfer Out and 2,000,000 HTTP and HTTPS Requests each month. Free Tier expires after 1 year.

If an average user session for you means about 50 requests of an average asset size 50KB per asset, you have 50 requests per session and 2.5MB data transfer per session. With this Free Tier, you can easily scale up to 20,000 user sessions per month, about 666 user sessions per day, before exhausting your data transfer limits.

The average asset size discussed here is the average of images, JS, CSS, HTML, and other assets like fonts that you may serve. To run a lean operation it is important to trim down the assets to be exactly what is needed in your application. In addition to saving you cost it has a much bigger impact of improved user experience with faster load times.

After Free Tier

After your run out of Free Tier limits or at the end of your free trial, following is the pricing plan for AWS CloudFront.

First 10 TB / month

United States & Canada Europe South Africa Japan, Hong Kong, Philippines, S. Korea, Singapore & Taiwan India Australia South America $0.085 $0.085 $0.110 $0.140 $0.170 $0.140 $0.250

Request Pricing for All HTTP Methods (per 10,000)

Type of request United States & Canada Europe South Africa Japan, Hong Kong, Philippines, S. Korea, Singapore & Taiwan India Australia South America HTTP $0.0075 $0.0090 $0.0090 $0.0090 $0.0090 $0.0090 $0.0160 HTTPS $0.0100 $0.0120 $0.0120 $0.0120 $0.0120 $0.0125 $0.0220

Based on this pricing, extending the above example, if you get 1,000 user sessions a day making about 50 requests per session and the average size of assets is 50KB, it comes out to be about 75GB per month ( 1,000 users * 50 assets * 50kB/asset * 30 days ). With a traffic split assuming 50% from US & Canada and 50% from Europe, it costs about $7.61 a month or $8.03 for HTTPS. If you stay at 50GB/month it costs $5.08/month or $5.35 for HTTPS, which is cheap if you take into account that with this set up you provide content delivery around the world with the same or similar latency as Amazon, Hulu, King, Supercell, etc.

More on pricing: https://aws.amazon.com/CloudFront/pricing

DNS & Route 53

If you are running a small to medium sized application, the DNS service for any Domain registrar should just work. I prefer Google domains for this as they provide a clean interface which is easy to manage and just works. This gets you 10 million lookups per year (https://support.google.com/domains/answer/6010092?hl=en) which is little over 27,000 per day. Seems pretty adequate for getting started and scaling up to a decent size.

Once you get to a point where these limits start becoming a bottleneck, you can easily reconfigure it to use AWS Route 53.

Product link: https://aws.amazon.com/route53

Pricing

Route 53 does not offer a Free Tier. To set up routing for one domain, it costs $0.50 to create a hosted zone and $0.400 per million queries for the first 1 billion queries/month. If you decide using this service instead of a Domain registrar offered DNS service, you will likely pay less than $0.90 per month for our 1,000 user session per month example.

More on pricing: https://aws.amazon.com/route53/pricing/

Final Bill!

Based on our assumption of 1,000 user sessions per day with average static assets 50KB, each session consisting of 50 assets requests and 50 API calls, following is the bill you can expect.

Service First 12 months After 12 months API Gateway $4 $12 Lambda $0 $0 DynamoDB $0 $0 S3 $0.23 $0.35 CloudFront(HTTP/HTTPS) $2.12 / $2.12 $7.61 / $8.03 DNS & Route 53 $0 $0 Total(CloudFront HTTP / HTTPS) $6.35 $19.96 / $20.38

You can see here, all the cost during the first 12 months is coming from S3, CloudFront and API Gateway. You will have $0 bill in first 12 months if your S3 storage is below 5GB, CloudFront traffic stays below 50GB/Month and API Gateway requests are less than 1 million a month, or you get 666 user sessions a month compared to the 1,000 user sessions I picked while running these calculations.

Gotchas, Tradeoffs, and Warnings

The above architecture is a general recommendation that should work for most applications. You may need to tweak this or introduce pieces to get more optimal performance. For example, if you are doing something like banking you will need to add a relational database using the RDS service. If you are streaming video/audio, you will have to rethink this. Delivering game assets would require some intelligent caching on the client as well. This will work for a lot of cases, but not all. DynamoDB is a NoSQL database. If your application requires a relational database, look at AWS RDS Sevice. It offers t2.micro for free during the first 12 months. It is fine for development. However, I will not recommend using it in production. AWS Lambda has a cold start problem. If your function suddenly gets a lot of traffic or has not received any traffic for some time, the first request can take a while (seconds) to respond. There are various ways out there to handle this. You should build timeouts in your client to handle this gracefully. Additionally, you could also set up a CloudWatch schedule to poke your function so that it stays alive. This post does not discuss usage of a caching layer. AWS ElastiCache (https://aws.amazon.com/elasticache/) provides Memcached and Redis as a service. Using this can considerably decrease your response times depending on the data access patterns of your application. One piece of modern architecture that is not discussed here is the asynchronous execution of jobs. If you are doing something that takes a while to process, say querying an external service or some data crunching it is best done by queuing the job using AWS SQS and making that data available to the user in the following request, this translates in a better user experience compared to user watching a loading screen on the app waiting for the response. One very important point is all these services are pay per use. You should set up CloudWatch alarms to monitor your usage and stay on top of it. You don't want to end up with a huge bill because you made a bunch of requests or your lambda functions kept running for longer than they should. Set up throttling, email alerts based on usage and monitor at least daily for discrepancies. Consider looking at AWS CloudWatch and AWS CloudTrail. AWS CloudWatch (https://aws.amazon.com/cloudwatch/) allows you to set up alarms for usage on various services that can help you prevent downtime or detect overage in your usage. AWS CloudTrail (https://aws.amazon.com/cloudtrail/) helps to track user activity and API usage. This is very useful in debugging or auditing the changes made to your cloud resources.

Conclusion

While looking at all the offerings from the cloud provider you pick, I will recommend that try not to set your self up for vendor lock-in. This means using AWS specific features that don't work outside can pose a future risk to move away from AWS. If you use AWS Cognito for authentication, make sure you think about how to move away from it, if need be, in case their roadmap and yours don't align. For instance, if you need Google login and they don't have one, you may be stuck!

I would also like to bring it up again that monitor your AWS usage using CloudWatch alarms. If you don't have proper limits in place, you may end up with a huge unexpected bill.

Finally, now that we have also talked about estimating pricing based on traffic, feel free to play around with the AWS calculator. You can enter your expected parameters to generate a monthly bill that can help you understand what you may pay based on the usage that you expect: https://calculator.s3.amazonaws.com/index.html