Since June 2020, I’ve been running a small Django app on GCP using App Engine Standard, Cloud SQL (PostgreSQL) and Cloud Storage. After experimenting with various configurations I found a way to make it run for as low as 11 Euro (13 US Dollars) per month.
Cost breakdown
In my November 2021 bill, the total monthly cost is €11.69 ($13.14 in US Dollars). This includes:
- €9.58 for the CloudSQL database instance (98% of the total cost)
- €0.16 for Cloud Storage
- €0.00 for the App Engine Frontend instances – all of my usage is covered by the free tier
- (the rest is taxes)
Understanding CloudSQL pricing
The cost of CloudSQL is dominating the bill. Let’s take a deeper look on what affects this charge. The docs on CloudSQL pricing mention two types of billing:
- instance pricing, for instances with shared CPUs. In this case we pay for the instance type we want, we and get a fixed amount of RAM and storage included. For example, we can opt for db-f1-micro which costs ~€10 per month
- CPU and memory pricing, for instances with dedicated CPUs. In this case we pay separately for CPU, RAM and storage of the database instance. This givex us flexibility in how much resources we want to allocate.
Instance pricing is cheaper. These do come with a warning that they’re not meant for production use, but this may be fine for a small personal project.
As my instance of PostgreSQL seems to be using ~450 MBs of memory under a normal load (see below), I went for the smallest micro instance type (which comes with 600 MBs of RAM).
Configuring CloudSQL
OK, how to actually set the instance type we want?
When creating the CloudSQL instance in the cloud console, we get the folowing configuration form:
At the first look, this seems to represent the cheapest possible configuration. But is it the micro instance then? Or the small instance perhaps? It’s actually neither, if we create the instance this way we will be billed via the CPU and memory pricing described above, not the instance pricing.
I then noticed that the CPU slider is not actually in the leftmost possible position. We can slide it further to the left:
This looks more like it! We even get the expected warning that those instances are only intended for testing.
However, I was still unsure if this configuration represents precisely the instance pricing options referenced in the docs, as the words “instance pricing”, “micro” and “small” don’t appear on the screen. It turns out that after creating the instance, we can use a gcloud
command to explicitly switch the Cloud SQL instance to the desired instance type. Note that this results in brief downtime while the instance is being
reconfigured (~5 minutes in my experience).
gcloud sql instances patch <INSTANCE_NAME> --tier=db-f1-micro
App Engine autoscaling settings
To make sure I’m not paying for idle instances (which App Engine autoscaling may create to better handle potential spikes in traffic), I set the cap on the maximum number of idle instances to 1 in my app.yaml
:
instance_class: F1
automatic_scaling:
max_idle_instances: 1
I also capped the total number of instances, to ensure the project doesn’t incur unbounded costs in case of unexpected spike in traffic:
instance_class: F1
automatic_scaling:
max_idle_instances: 1
max_instances: 3
Conclusion
Cloud SQL pricing dominates the cost of running a Django app on GCP. Take care to configure the SQL instance as desired. With the configuration described above, the monthly cost for my app is ~€13. Let me know in the comments if you find a way to go lower than that!