...
 
Commits (2)
<template>
<div class="dbaas dbaas-budgeter">
<div>
<button v-for="preset in presets" v-on:click="budget = preset">
${{preset}}
</button>
</div>
<table>
<tr>
<td class="label">Budget: <strong>{{budget | price}}</strong></td>
<td class="input"><input type="range" v-model="budget" min="10" max="500"/></td>
<td class="input"><input type="range" v-model="budget" min="15" max="500"/></td>
</tr>
</table>
<table>
<thead>
<tr>
<th class="label">Vendor</th>
<th style="width:100%;">Instance Type</th>
<th>Estimate</th>
<th style="width:100%;">Matching Instance Type</th>
<th>Price</th>
</tr>
</thead>
<tr v-if="awsPlan.price">
<td>Amazon RDS</td>
<td><strong>{{awsPlan.name}}:</strong><br>{{awsPlan.description}}</td>
<td>{{awsPlan.description}} ({{awsPlan.name}})</td>
<td class="price">{{budget | price}}</td>
</tr>
<tr v-if="azurePlan.price">
<td>Azure Database</td>
<td><strong>{{azurePlan.name}}:</strong><br>{{azurePlan.description}}</td>
<td>{{azurePlan.description}} ({{azurePlan.name}})</td>
<td class="price">{{budget | price}}</td>
</tr>
<tr v-if="doPlan.price">
......@@ -31,7 +37,7 @@
</tr>
<tr v-if="elephantPlan.price">
<td>ElephantSQL</td>
<td><strong>{{elephantPlan.name}}:</strong><br>{{elephantPlan.description}}</td>
<td>{{elephantPlan.description}} ({{elephantPlan.name}})</td>
<td class="price">{{elephantPlan.price | price}}</td>
</tr>
<tr v-if="gcpPlan.price">
......@@ -41,20 +47,33 @@
</tr>
<tr v-if="herokuPlan.price">
<td>Heroku Postgres</td>
<td><strong>{{herokuPlan.name}}:</strong><br>{{herokuPlan.description}}</td>
<td>{{herokuPlan.description}} ({{herokuPlan.name}})</td>
<td class="price">{{herokuPlan.price | price}}</td>
</tr>
<tr v-if="scaleGridPlan.price">
<td>ScaleGrid</td>
<td>{{scaleGridPlan.description}} ({{scaleGridPlan.name}})</td>
<td class="price">{{scaleGridPlan.price | price}}</td>
</tr>
</table>
</div>
</template>
<script>
import priceFilter from './price.filter'
import { budgetAWS, budgetAzure, budgetDigitalOcean, budgetElephant, budgetGCP, budgetHeroku } from './dbaas'
import {
budgetAWS,
budgetAzure,
budgetDigitalOcean,
budgetElephant,
budgetGCP,
budgetHeroku,
budgetScaleGrid
} from './dbaas'
export default {
name: 'DbaasBudgeter',
data () {
return { budget: 20 /* USD*/ }
return { budget: 50 /* USD*/, presets: [15, 25, 50, 100, 150, 200, 300] }
},
computed: {
awsPlan () {
......@@ -75,6 +94,9 @@
herokuPlan () {
return budgetHeroku(this.budget)
},
scaleGridPlan () {
return budgetScaleGrid(this.budget)
},
},
filters: { price: priceFilter }
}
......
<template>
<div class="dbaas dbaas-calculator">
<div>
<button v-for="preset in presets" v-on:click="applyPreset(preset)" v-bind:title="preset.description">
<button v-for="preset in presets" v-on:click="applyPreset(preset)" :title="preset.description">
{{preset.name}}
</button>
</div>
<table>
<tr>
<td class="label">Cores: <strong>{{cores}}</strong></td>
<td class="input"><input type="range" v-model.number="cores" min="1" max="8"/></td>
<td class="label">
<label for="inputCores">Cores: <strong>{{cores}}</strong></label>
</td>
<td class="input">
<input id="inputCores" type="range" v-model.number="cores" min="1" max="8"/>
</td>
</tr>
<tr>
<td class="label">Memory: <strong>{{memoryGB}} GB</strong></td>
<td class="input"><input type="range" v-model.number="memoryGB" min="1" max="32"/></td>
<td class="label">
<label for="inputMemory">Memory: <strong>{{memoryGB}} GB</strong></label>
</td>
<td class="input">
<input id="inputMemory" type="range" v-model.number="memoryGB" min="1" max="64"/>
</td>
</tr>
<tr>
<td class="label">Storage: <strong>{{storageGB}} GB</strong></td>
<td class="input"><input type="range" v-model.number="storageGB" min="1" max="500"/></td>
<td class="label">
<label for="inputStorage">Storage: <strong>{{storageGB}} GB</strong></label>
</td>
<td class="input">
<input id="inputStorage" type="range" v-model.number="storageGB" min="1" max="750"/>
</td>
</tr>
</table>
<h4>Vendor Monthly Estimates</h4>
<div style="float:right">Average: {{averagePrice | price}}</div>
<h4>Vendor Monthly Price Estimates</h4>
<table>
<thead>
<tr>
<th class="label">Vendor</th>
<th style="width:100%;">Instance Type</th>
<th>Estimate</th>
<th style="width:100%;">Matching Instance Type</th>
<th>Price</th>
</tr>
</thead>
<tr>
<tr :class="{cheapest: awsTotal === cheapestPrice }">
<td>Amazon RDS</td>
<td><strong>{{awsPlan.name}}:</strong><br>{{awsPlan.description}}</td>
<td>{{awsPlan.description}} ({{awsPlan.name}})</td>
<td class="price">{{awsTotal | price}}</td>
</tr>
<tr>
<tr :class="{cheapest: azureTotal === cheapestPrice }">
<td>Azure Database</td>
<td><strong>{{azurePlan.name}}:</strong><br>{{azurePlan.description}}</td>
<td>{{azurePlan.description}} ({{azurePlan.name}})</td>
<td class="price">{{azureTotal | price}}</td>
</tr>
<tr>
<tr :class="{cheapest: doPlan.price === cheapestPrice }">
<td>Digital Ocean</td>
<td>{{doPlan.description}}</td>
<td class="price">{{doPlan.price | price}}</td>
</tr>
<tr>
<tr :class="{cheapest: elephantPlan.price === cheapestPrice }">
<td>ElephantSQL</td>
<td><strong>{{elephantPlan.name}}:</strong><br>{{elephantPlan.description}}</td>
<td>{{elephantPlan.description}} ({{elephantPlan.name}})</td>
<td class="price">{{elephantPlan.price | price}}</td>
</tr>
<tr>
<tr :class="{cheapest: gcpTotal === cheapestPrice }">
<td>Google Cloud SQL</td>
<td>{{gcpPlan.description}}</td>
<td class="price">{{gcpTotal | price}}</td>
</tr>
<tr>
<tr :class="{cheapest: herokuPlan.price === cheapestPrice }">
<td>Heroku Postgres</td>
<td><strong>{{herokuPlan.name}}:</strong><br>{{herokuPlan.description}}</td>
<td>{{herokuPlan.description}} ({{herokuPlan.name}})</td>
<td class="price">{{herokuPlan.price | price}}</td>
</tr>
<tr :class="{cheapest: scaleGridPlan.price === cheapestPrice }">
<td>ScaleGrid</td>
<td>{{scaleGridPlan.description}} ({{scaleGridPlan.name}})</td>
<td class="price">{{scaleGridPlan.price | price}}</td>
</tr>
</table>
</div>
</template>
<script>
import { getAWSPlan, getAzurePlan, getDigitalOceanPlan, getElephantPlan, getGCPPlan, getHerokuPlan } from './dbaas'
import priceFilter from './price.filter'
import {
getAWSPlan,
getAzurePlan,
getDigitalOceanPlan,
getElephantPlan,
getGCPPlan,
getHerokuPlan,
getScaleGridPlan
} from './dbaas'
import priceFilter from './price.filter'
const PRESETS = [
{ name: 'small', storageGB: 10, cores: 1, memoryGB: 2 },
{ name: 'medium', storageGB: 20, cores: 2, memoryGB: 4 },
{ name: 'large', storageGB: 50, cores: 4, memoryGB: 8 },
{ name: 'huge', storageGB: 100, cores: 6, memoryGB: 16 },
{ name: 'enormous', storageGB: 200, cores: 8, memoryGB: 32 }
]
PRESETS.forEach(p => { p.description = `Preset for ${p.cores} cores, ${p.memoryGB} GB memory, ${p.storageGB} GB storage` })
const PRESETS = [
{ name: 'micro', storageGB: 10, cores: 1, memoryGB: 1 },
{ name: 'small', storageGB: 20, cores: 1, memoryGB: 2 },
{ name: 'medium', storageGB: 40, cores: 1, memoryGB: 4 },
{ name: 'large', storageGB: 100, cores: 2, memoryGB: 8 },
{ name: 'huge', storageGB: 300, cores: 4, memoryGB: 16 },
{ name: 'enormous', storageGB: 600, cores: 8, memoryGB: 32 }
]
PRESETS.forEach(p => { p.description = `Preset for ${p.cores} cores, ${p.memoryGB} GB memory, ${p.storageGB} GB storage` })
export default {
name: 'DbaasCalculator',
data () {
return { storageGB: 10, cores: 1, memoryGB: 2, presets: PRESETS }
export default {
name: 'DbaasCalculator',
data () {
return { storageGB: 10, cores: 1, memoryGB: 2, presets: PRESETS }
},
computed: {
awsPlan () {
return getAWSPlan(this.memoryGB, this.cores)
},
awsTotal () {
return this.awsPlan.fullPrice(this.storageGB)
},
azurePlan () {
return getAzurePlan(this.memoryGB, this.cores)
},
azureTotal () {
return this.azurePlan.fullPrice(this.storageGB)
},
doPlan () {
return getDigitalOceanPlan(this.memoryGB, this.storageGB, this.cores)
},
elephantPlan () {
return getElephantPlan(this.memoryGB, this.storageGB, this.cores)
},
gcpPlan () {
return getGCPPlan(this.memoryGB, this.cores)
},
gcpTotal () {
return this.gcpPlan.fullPrice(this.storageGB)
},
herokuPlan () {
return getHerokuPlan(this.memoryGB, this.storageGB)
},
scaleGridPlan () {
return getScaleGridPlan(this.memoryGB, this.storageGB, this.cores)
},
computed: {
awsPlan () {
return getAWSPlan(this.memoryGB, this.cores)
},
awsTotal () {
return this.awsPlan.fullPrice(this.storageGB)
},
azurePlan () {
return getAzurePlan(this.memoryGB, this.cores)
},
azureTotal () {
return this.azurePlan.fullPrice(this.storageGB)
},
doPlan () {
return getDigitalOceanPlan(this.memoryGB, this.storageGB, this.cores)
},
elephantPlan () {
return getElephantPlan(this.memoryGB, this.storageGB, this.cores)
},
gcpPlan () {
return getGCPPlan(this.memoryGB, this.cores)
},
gcpTotal () {
return this.gcpPlan.fullPrice(this.storageGB)
},
herokuPlan () {
return getHerokuPlan(this.memoryGB, this.storageGB)
},
allPrices () {
const prices = [this.awsTotal, this.azureTotal, this.doPlan.price, this.elephantPlan.price, this.gcpTotal, this.herokuPlan.total, this.scaleGridPlan.total]
return prices.filter(p => p > 0)
},
methods: {
applyPreset (preset) {
this.cores = preset.cores
this.storageGB = preset.storageGB
this.memoryGB = preset.memoryGB
}
averagePrice () {
const prices = this.allPrices
return prices.reduce((a, b) => a + b) / prices.length
},
filters: { price: priceFilter }
}
cheapestPrice () {
return this.allPrices.reduce((a, b) => Math.min(a, b))
}
},
methods: {
applyPreset (preset) {
this.cores = preset.cores
this.storageGB = preset.storageGB
this.memoryGB = preset.memoryGB
}
},
filters: { price: priceFilter }
}
</script>
<style>
.dbaas-calculator .cheapest {
background-color: rgba(66, 185, 131, 0.50);
font-weight: bolder;
}
</style>
......@@ -57,21 +57,24 @@ class StaticPlan extends BasePlan {
}
const PLANS_AWS = [
{ name: 'db.t3.micro', memoryGB: 1, cores: 2, price: 13.14 },
{ name: 'db.t3.small', memoryGB: 2, cores: 2, price: 26.28 },
{ name: 'db.t3.medium', memoryGB: 4, cores: 2, price: 52.56 },
{ name: 'db.t3.large', memoryGB: 8, cores: 2, price: 105.85 },
{ name: 'db.t3.xlarge', memoryGB: 16, cores: 4, price: 211.70 },
{ name: 'db.t3.2xlarge', memoryGB: 32, cores: 8, price: 422.67 },
{ name: 'db.t3.micro', memoryGB: 1, cores: 2, price: 9.417 },
{ name: 'db.t3.small', memoryGB: 2, cores: 2, price: 18.834 },
{ name: 'db.t3.medium', memoryGB: 4, cores: 2, price: 37.741 },
{ name: 'db.t3.large', memoryGB: 8, cores: 2, price: 75.482 },
{ name: 'db.t3.xlarge', memoryGB: 16, cores: 4, price: 150.964 },
{ name: 'db.t3.2xlarge', memoryGB: 32, cores: 8, price: 301.855 },
{ name: 'db.r5.xlarge', memoryGB: 32, cores: 4, price: 210.824 },
{ name: 'db.r5.2xlarge', memoryGB: 64, cores: 8, price: 421.575 },
{ name: 'db.r5.4xlarge', memoryGB: 128, cores: 16, price: 843.150 },
].map(options => new DynamicPlan({ storagePrice: 0.138, ...options }))
const PLANS_AZURE = [
{ name: 'Basic Gen 5', cores: 1, memoryGB: 2, price: 24.82, storagePrice: 0.10 },
{ name: 'Basic Gen 5', cores: 2, memoryGB: 4, price: 49.64, storagePrice: 0.10 },
{ name: 'General Purpose Gen 5', cores: 2, memoryGB: 10, price: 127.896, storagePrice: 0.115 },
{ name: 'General Purpose Gen 5', cores: 4, memoryGB: 20, price: 255.792, storagePrice: 0.115 },
{ name: 'General Purpose Gen 5', cores: 8, memoryGB: 40, price: 511.584, storagePrice: 0.115 },
{ name: 'General Purpose Gen 5', cores: 16, memoryGB: 80, price: 1023.168, storagePrice: 0.115 }
{ name: 'General Purpose Gen 5', cores: 2, memoryGB: 10, price: 77.672, storagePrice: 0.115 },
{ name: 'General Purpose Gen 5', cores: 4, memoryGB: 20, price: 155.344, storagePrice: 0.115 },
{ name: 'General Purpose Gen 5', cores: 8, memoryGB: 40, price: 310.688, storagePrice: 0.115 },
{ name: 'General Purpose Gen 5', cores: 16, memoryGB: 80, price: 621.376, storagePrice: 0.115 }
].map(options => new DynamicPlan(options))
const PLANS_DO = [
......@@ -80,7 +83,8 @@ const PLANS_DO = [
{ memoryGB: 4, cores: 2, storageGB: 38, price: 60 },
{ memoryGB: 8, cores: 4, storageGB: 115, price: 120 },
{ memoryGB: 16, cores: 6, storageGB: 270, price: 240 },
{ memoryGB: 32, cores: 8, storageGB: 580, price: 480 }
{ memoryGB: 32, cores: 8, storageGB: 580, price: 480 },
{ memoryGB: 64, cores: 16, storageGB: 1120, price: 1600 },
].map(options => new StaticPlan(options))
const PLANS_ELEPHANT = [
......@@ -88,7 +92,8 @@ const PLANS_ELEPHANT = [
{ name: 'Happy Hippo', memoryGB: 4, storageGB: 100, price: 99 },
{ name: 'Enormous Elephant', memoryGB: 8, storageGB: 250, price: 199 },
{ name: 'Puffy Pigeon', memoryGB: 16, storageGB: 500, price: 399 },
{ name: 'Dramatic Dolphin', memoryGB: 32, storageGB: 1000, price: 749 }
{ name: 'Dramatic Dolphin', memoryGB: 32, storageGB: 1000, price: 749 },
{ name: 'Ruthless Rat', memoryGB: 64, storageGB: 2000, price: 1399 },
].map(options => new StaticPlan(options))
const PLANS_HEROKU = [
......@@ -96,11 +101,22 @@ const PLANS_HEROKU = [
{ name: 'Standard 2', memoryGB: 8, storageGB: 256, price: 200 },
{ name: 'Standard 3', memoryGB: 15, storageGB: 512, price: 400 },
{ name: 'Standard 4', memoryGB: 30, storageGB: 750, price: 750 },
{ name: 'Standard 5', memoryGB: 61, storageGB: 1024, price: 1400 }
{ name: 'Standard 5', memoryGB: 61, storageGB: 1000, price: 1400 },
{ name: 'Standard 6', memoryGB: 122, storageGB: 1500, price: 2000 },
].map(options => new StaticPlan(options))
const PLANS_GCP = PLANS_DO.map(plan => getGCPPlan(plan.memoryGB, plan.cores)) // not real offerings, just using DO as model
const PLANS_SCALEGRID = [
{ name: 'Micro', memoryGB: 1, cores: 1, storageGB: 10, price: 20 },
{ name: 'Small', memoryGB: 2, cores: 1, storageGB: 40, price: 40 },
{ name: 'Medium', memoryGB: 4, cores: 1, storageGB: 60, price: 87 },
{ name: 'Large', memoryGB: 8, cores: 2, storageGB: 120, price: 191 },
{ name: 'XLarge', memoryGB: 16, cores: 2, storageGB: 240, price: 302 },
{ name: 'X2XLarge', memoryGB: 32, cores: 4, storageGB: 480, price: 605 },
{ name: 'X4XLarge', memoryGB: 64, cores: 8, storageGB: 700, price: 1178 }
].map(options => new StaticPlan(options))
const BLANK_PLAN = { description: 'N/A', fullPrice: () => null }
const getPlan = (plans, options) => plans.find(plan => plan.matches(options)) || BLANK_PLAN
......@@ -123,6 +139,8 @@ export function getGCPPlan (memoryGB, cores) {
export const getHerokuPlan = (memoryGB, storageGB) => getPlan(PLANS_HEROKU, { memoryGB, storageGB })
export const getScaleGridPlan = (memoryGB, storageGB, cores) => getPlan(PLANS_SCALEGRID, { memoryGB, storageGB, cores })
function getPlanByBudget (plans, budget) {
for (let i = plans.length - 1; i > -1; i--) {
if (plans[i].price <= budget) return plans[i]
......@@ -145,3 +163,5 @@ export const budgetDigitalOcean = budget => getPlanByBudget(PLANS_DO, budget) ||
export const budgetElephant = budget => getPlanByBudget(PLANS_ELEPHANT, budget) || BLANK_PLAN
export const budgetHeroku = budget => getPlanByBudget(PLANS_HEROKU, budget) || BLANK_PLAN
export const budgetScaleGrid = budget => getPlanByBudget(PLANS_SCALEGRID, budget) || BLANK_PLAN
......@@ -5,14 +5,14 @@ description: This is a comparison of PostgreSQL DBaaS providers with an interact
I recently wanted to start a side-project using the serverless application hosting service [Zeit Now](https://zeit.co/now).
Under "how do I use databases" in their FAQ, they recommend cloud databases such as Azure CosmosDB, MongoDB Atlas, Firebase, and Google Cloud SQL.
Personally, I want a relational database like PostgreSQL, and since it's 2019 there are lots of options.
Personally, I want a relational database like PostgreSQL, and nowadays there are lots of options.
While it's certainly _possible_ to self-host a PostgreSQL DB on a DigitalOcean droplet for $5/month, that doesn't mean it's a good idea.
In my opinion, outsourcing DB maintenance to a vendor is a no-brainer as long as your hosting bill is in the triple-digits.
Most side-projects will never get that far.
This page is an effort to compare DBaaS vendors for hosting a PostgreSQL database for my personal projects.
The focus is on these smaller side-project type deployments (\< $200/month or so), not giant enterprise-level clusters.
My focus is on these smaller side-project type deployments ($100/month or so), not giant enterprise-level clusters.
There are links to the pricing pages too (:moneybag:).
If you find an error or have a suggestion, please [open an issue](https://gitlab.com/barnabas/barnabas.gitlab.io/issues)
or better yet [submit a merge request](https://gitlab.com/barnabas/barnabas.gitlab.io/merge_requests).
......@@ -22,46 +22,50 @@ Thanks!
## Vendor Overview
### [Amazon RDS for PostgreSQL](https://aws.amazon.com/rds/postgresql/)
I have personally used RDS for PostgreSQL at work and I have no complaints with it currently.
I have used RDS for PostgreSQL at work and I have no complaints.
There are several instance types that have two vCPUs and vary by memory, but as soon as you need more CPU, you move to db.t3.xlarge.
Amazon's [T3 DB instance types use some kind of CPU bursting and CPU credit system](https://aws.amazon.com/rds/instance-types/), and yet DBaaS was supposed to be _easier_ somehow...
Amazon has significant discounts for reserved instances, so the calculator assumes a 1-year commitment with no upfront payment.
The calculator assumes a 1-year commitment of reserved instances with no upfront payment in US West (Oregon).
[:moneybag:](https://aws.amazon.com/rds/postgresql/pricing/)
### [Google Cloud SQL for PostgreSQL](https://cloud.google.com/sql/docs/postgres/)
Google's cloud offering is month-to-month, and they offer a discount for [sustained use](https://cloud.google.com/compute/docs/sustained-use-discounts#sud_table).
You are not tied to specific instance types; instead CPU, storage, and memory are all adjustable.
This makes a great deal of sense to me.
[:moneybag:](https://cloud.google.com/sql/docs/postgres/pricing)
### [Azure Database for PostgreSQL](https://azure.microsoft.com/en-us/services/postgresql/)
Azure does not break out their pricing for memory; you pick the machine class based on CPU and pay for storage.
Theoretically there are steep discounts for 1-year reservations, but I couldn't figure it out, so the calculator might be overestimating.
This calculator starts applying the 1-year 39% discount once you select General Purpose Compute Gen 5.
[:moneybag:](https://azure.microsoft.com/en-us/pricing/details/postgresql/)
### [Heroku Postgres](https://www.heroku.com/postgres)
Heroku is one of the few vendors with an OK free tier for Postgres, but I wouldn't count on it for hosting real things.
Unlike Azure, Heroku has pricing tiers based on memory, storage, and number of connections.
CPU is not visible or configurable, so the calculator below may not tell the whole story.
Surprisingly, there is a point at the 4 GB memory and 64 GB storage mark where Heroku is the best choice at $50/month.
Above that there's a big jump up to $200 that makes Heroku the most expensive option.
It seems like they're not really competing in the $100/month range.
[:moneybag:](https://www.heroku.com/pricing#databases)
### [DigitalOcean Managed Databases](https://www.digitalocean.com/products/managed-databases/)
DO is a newcomer to the PostgreSQL hosting world.
They are especially dominant in the 4-core and 6-core segment, costing about $60/month less than the next competitor, GCP.
DO is a relative newcomer to the PostgreSQL hosting world.
While they have their own data centers (presumably), their pricing resembles other tier-2 vendors.
Bear in mind that [DigitalOcean's outbound DB bandwidth is free until December 2020](https://www.digitalocean.com/docs/billing/bandwidth/).
[:moneybag:](https://www.digitalocean.com/pricing/#Databases)
### [ElephantSQL](https://www.elephantsql.com/)
ElephantSQL provides managed PostgreSQL hosting in a variety of other cloud platforms' data centers, including AWS, Softlayer, GCP, and Azure.
Here's another vendor that doesn't break out CPU and I wish they would at least mention it on the plan page.
As a plus, their service level plan names are downright adorable.
Only the dedicated plans are included in this calculator, but they also offer shared instance plans at the $5, $10, and $19/month level too.
[:moneybag:](https://www.elephantsql.com/plans.html)
### [IBM Cloud Databases for PostgreSQL](https://www.ibm.com/cloud/databases-for-postgresql)
IBM Cloud offers a managed PostgreSQL service that allows for mutually exclusive scaling of Disk and RAM (rather than instance types.) They also don't break out CPU as a metric today. It starts at 5GB of Disk and 1GB of RAM for $15.80/month.
[:moneybag:](https://cloud.ibm.com/docs/services/databases-for-postgresql?topic=databases-for-postgresql-pricing#pricing)
### [Google Cloud SQL for PostgreSQL](https://cloud.google.com/sql/docs/postgres/)
Google's cloud offering is month-to-month, and they offer a discount for [sustained use](https://cloud.google.com/compute/docs/sustained-use-discounts#sud_table).
You are not tied to specific instance types; instead CPU, storage, and memory are all adjustable.
This makes a great deal of sense to me.
On the other hand, I suspect the Google pricing is inflated below because I can't figure out how [committed use discounts work](https://cloud.google.com/compute/docs/instances/signing-up-committed-use-discounts).
[:moneybag:](https://cloud.google.com/sql/docs/postgres/pricing)
### [Heroku Postgres](https://www.heroku.com/postgres)
Heroku is one of the few vendors with an OK free tier for Postgres, but I wouldn't count on it for hosting real things.
Unlike Azure, Heroku has pricing tiers based on memory, storage, and number of connections.
CPU is not visible or configurable, so the calculator below may not tell the whole story.
There is a point at the 4 GB memory and 64 GB storage mark where Heroku is almost the best choice at $50/month.
Otherwise Heroku is fairly expensive, per their reputation.
[:moneybag:](https://www.heroku.com/pricing#databases)
### [ScaleGrid](https://scalegrid.io/postgresql.html)
ScaleGrid hosts a variety of databases (Mongo, Redis, MySQL, PostgreSQL) on top of a variety of clouds (AWS, Azure, DO).
They offer [unmatched admin control and SSH root access](https://scalegrid.io/postgresql/hosting-comparison.html).
This calculator is using their AWS Standalone pricing for dedicated hosting.
[:moneybag:](https://scalegrid.io/pricing.html?db=POSTGRESQL&cloud=cloud_aws_standard&replica=deployment_standalone&instance=Micro#section_pricing_dedicated)
## Calculator
Here's a calculator to give a rough estimate of monthly costs for each major DBaaS vendor.
......@@ -75,16 +79,30 @@ This does not factor in cost of network egress, backups or replicas; it's just f
*How Far Does That Dollar Go?*
<ClientOnly>
<dbaas-budgeter class="foo" />
<dbaas-budgeter />
</ClientOnly>
## Conclusions
## Observations
There are two clear tiers of DBaaS providers: those who run PostgreSQL on their own metal (AWS, Azure, DO, GCP) and those who rent from the first group.
Azure's 1-year commitment discount really gives it an edge at the high end.
Google seems to do well when you max out memory and leave everything else low.
DigitalOcean's sweet spot is their 4 CPU / 8 GB memory / 115 GB storage product.
GCP is flexible and competitive with the price for memory right now. I like it more now than ever.
There is a point where Heroku is the cheapest option, which blew me away.
I just can't understand why there's a conspicuous hole in Heroku's lineup between Standard 0 and 2, except maybe customers who spend less than $200/month are 10x annoying?
The smaller vendors cost more but provide value in other ways, such better tooling or support.
Especially at the $100/month range that may really be worth it.
I just can't understand why there's a conspicuous hole in Heroku's lineup between Standard 0 and 2.
Standard 1 must have had 6 GB memory and 128 GB storage for $100. That would have rocked.
### Further Reading
* [Pros and Cons of DBaaS](https://www.objectrocket.com/blog/company/advantages-and-disadvantages-of-dbaas/)
* [Comparing Cloud Database Options for PostgreSQL](https://severalnines.com/blog/comparing-cloud-database-options-postgresql)
### Other Providers
* [IBM Cloud Databases for PostgreSQL](https://www.ibm.com/cloud/databases-for-postgresql): not included in the calculator because the pricing model is so different, but it starts at 5GB of Disk and 1GB of RAM for $15.80/month.
* [Aiven for PostgreSQL](https://aiven.io/postgresql): similar concept to ScaleGrid
## Changelog
* 2019-03-31: initial version
* 2019-04-01: IBM Cloud
* 2019-10-25: add budget calculator
* 2020-03-08: adjust presets, added ScaleGrid, updated AWS and Azure prices
This source diff could not be displayed because it is too large. You can view the blob instead.