Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
Most teams click 'create droplet' and accept the defaults, then discover three months later they're on the wrong instance family, the wrong region, the wrong disk type, or running on a kernel they can't update. A few minutes spent at provisioning time — picking the region close to your users, the instance type matched to your workload (CPU-bound vs memory-bound vs network-bound), and a base image you understand — saves weeks of migration pain later. This isn't premature optimization; it's the difference between 'I'll redo the box on Saturday' and 'I have to migrate prod next quarter.'
Sizing rule of thumb for a CRUD API at Day 0: 1 vCPU, 2 GB RAM, 25 GB SSD, in the same region as your DB. You can serve a few hundred concurrent users on this — modern languages can push 1k+ req/sec on a single core if the DB keeps up. Pick the region by where your users (or your DB) are. Pick an instance family marked 'general purpose' (DigitalOcean: regular; AWS: t-class; GCP: e2-medium). Pick an SSD-backed disk, never spinning. And pin your base image (Ubuntu 24.04 LTS, Debian 12) — don't 'use the latest'.
curl -s http://ipinfo.io/json | jq from your VM. Confirm region and AS-number match what you provisioned.cat /var/log/unattended-upgrades/unattended-upgrades.log after a week — you should see security patches applied automatically.Use these three in order. Each builds on the one before.
What's the difference between 'general purpose', 'compute optimized', 'memory optimized', and 'burstable' instance families? When does each one matter for a web service?
Why does it matter whether my VM and my DB are in the same region (vs same provider, different region)? Walk me through what a `SELECT id FROM users WHERE id=42` looks like in network terms when they're cross-region.
I have a Python API on a t3.small in us-east-1 talking to an RDS Postgres in us-west-2. p99 is 280ms. Break down where that latency is going and what I'd change to get it under 50ms. Quantify each step.
// Provisioning checklist as code (Terraform sketch — DigitalOcean)
resource "digitalocean_droplet" "app" {
image = "ubuntu-24-04-x64" // pinned LTS — don't track "latest"
name = "app-prod-1"
region = "blr1" // same region as your DB & your users
size = "s-1vcpu-2gb" // start small. resize is one click later.
ssh_keys = [var.ssh_key_id] // never use root password auth
user_data = file("cloud-init.yml") // base packages + firewall on first boot
}
// /etc/cloud-init.yml — the base every box starts with
package_update: true
package_upgrade: true
packages:
- ufw
- fail2ban
- unattended-upgrades
runcmd:
- [ufw, allow, "22"]
- [ufw, allow, "443"]
- [ufw, allow, "80"]
- [ufw, --force, enable]
- [systemctl, enable, --now, unattended-upgrades]node main.js