Compute, storage and databases
EC2, ECS on Fargate, EKS, S3 with presigned URLs, and RDS with Multi-AZ, backups and parameter groups.
Three questions a service asks of the cloud: where does the JVM run, where do the files go, and where is the database. AWS has several answers to each, and the interesting part is not the product names but the operational trade each one makes — what you stop having to do, and what you stop being able to do.
Compute: EC2, ECS, EKS, and where a JVM belongs
EC2 is a virtual machine: an instance type (m6g.large — family, generation, size), an AMI, a subnet, a security group, an instance profile for its role. It is this repository's production host in a different data centre: a JVM under systemd, nginx in front, you own the OS. Operational weight: patching, the unit files, the disk, the backup timer — everything the CI/CD course's Linux lesson covered is yours. It is the right answer for one or two services on a small team, and for anything with a licensing or hardware constraint; an auto scaling group with a launch template and a load balancer makes it two-of-everything and self-healing.
ECS runs containers without a cluster to manage: a task definition (the image, CPU and memory, environment, the task role, the log configuration) and a service that keeps N tasks running behind a target group. With Fargate there are no instances at all — you pay per task-second for the CPU and memory you declared, and the Docker course's MaxRAMPercentage is what sizes the JVM to it. ECS is the least-surprising way to run a handful of Spring Boot services on AWS: the Docker knowledge transfers whole, the Kubernetes vocabulary is not needed, and deployments are rolling with health checks and circuit breakers built in.
EKS is managed Kubernetes: AWS runs the control plane, you run (or Fargate runs) the nodes, and everything in the Kubernetes course applies unchanged — Deployments, Services, probes, HPA — plus the AWS-specific glue: the load balancer controller that turns an Ingress into an ALB, IRSA or Pod Identity that turns a service account into a role, the EBS and EFS CSI drivers for volumes. Choose it when the organisation is standardising on Kubernetes or already has the skills; a team of three with four services does not need it.
Lambda runs a function per event — an S3 upload, an SQS message, an HTTP request through API Gateway — and charges per invocation and millisecond. Java's cold start (a JVM plus a framework, seconds) is the known cost; SnapStart snapshots the initialised JVM and restores it in hundreds of milliseconds, and Spring Cloud Function or a plain handler class keep the start small. Right for event-driven glue and spiky, infrequent work; wrong for a service with steady traffic, where a container is cheaper and simpler to observe.
Storage: S3, and the patterns that use it well
S3 is object storage: a bucket, keys, objects up to 5 TB, eleven nines of durability, and an HTTP API. It is not a filesystem — no rename (copy and delete), no append, no directories except as a prefix convention — and treating it as one is the mistake behind most S3 performance tickets. The patterns a backend actually uses:
- Presigned URLs for uploads and downloads: the service signs a URL that permits one
PUTto one key for fifteen minutes, hands it to the client, and the bytes go directly to S3, never through the JVM — the system-design course's file-upload brief and its "object storage: never through your service" rule. - Prefixes for partitioning,
uploads/2026/09/18/<uuid>rather than one flat namespace, and a key design that spreads hot writes across prefixes; the old per-prefix rate limits are gone, but listing is still per prefix. - Lifecycle rules: transition to Infrequent Access after 30 days, to Glacier after a year, expire after seven — cost and retention as configuration, not a cron job.
- Versioning and Object Lock for the backups (the production course's backup lesson: a backup on the same machine is not a backup; a bucket in another region with Object Lock is).
- Event notifications to SQS or Lambda on
PutObject, which is how "process this upload" becomes an event rather than a poll. - Server-side encryption on by default, a bucket policy that denies unencrypted puts and plain HTTP, and Block Public Access at the account level, so a bucket cannot be made public by a typo.
EBS is the block disk attached to one EC2 instance in one zone — the volume a database on EC2 would use, snapshotted to S3 for backup. EFS is a shared NFS filesystem for the rare case several instances must write the same files. Neither is where uploads belong.
Databases: RDS, and what managed actually means
RDS runs PostgreSQL (or MySQL, and the others) with the operations you would otherwise own: automated backups with point-in-time recovery, a Multi-AZ standby with synchronous replication and automatic failover, read replicas, minor version patching in a maintenance window, and metrics into CloudWatch. Aurora is AWS's own storage layer under a PostgreSQL-compatible engine, with faster failover and replicas that share storage, at a higher price per instance and a lower one for I/O-heavy workloads.
What stays yours, and what the SQL course's lessons still decide:
- Sizing and the pool. The instance class sets
max_connections, and the connection-pooling lesson's arithmetic still applies: ten services with a pool of twenty each is two hundred connections to a database that may allow a hundred and fifty. RDS Proxy is the PgBouncer the course mentioned, managed, and it also makes Lambda-to-RDS survivable. - Failover is a reconnect. Multi-AZ failover changes what the endpoint's DNS resolves to; a JVM that cached the address, or a pool that holds dead connections, sees errors for the thirty to sixty seconds until it notices. HikariCP's
maxLifetimebelow the DNS TTL, and validating connections on borrow, are the settings; testing a failover deliberately (the chaos lesson) before the first real one is the practice. - Backups are still yours to verify. Automated snapshots exist; a restore that has never been run is the production course's "restore is the product". Cross-region snapshot copies are the off-site copy.
- Parameter groups are
postgresql.conf; Performance Insights is the plan-level view the query-optimisation lesson reads; the migration is still Flyway, still before the switchover, still expand-contract.
DynamoDB is the other database: key-value and document, single-digit-millisecond at any scale, no joins, and a data model you design around access patterns before you write it; the data-store lesson's "start from the questions" rule is the whole of DynamoDB design. ElastiCache is Redis (or Valkey) managed, for the caching course's patterns and the distributed-locks lesson's limits.
Managed queues and streams
The messaging courses' brokers exist as services, and the choice between them is the Kafka-versus-RabbitMQ decision with an operations bill attached. SQS is a queue: at-least-once (or FIFO queues with exactly-once processing and ordering per message group, at lower throughput), a visibility timeout that is the consumer's lease, a dead-letter queue configured per queue, and nothing to run — the right default for "work to be done". SNS is fan-out: a topic that pushes to many SQS queues, Lambdas or HTTP endpoints, so SNS-to-SQS is the pub/sub shape without a broker. EventBridge is SNS with routing rules on the event's content and a schema registry, for events between systems. MSK is Kafka, managed — brokers you size, patches AWS applies, and everything in the Kafka course applies unchanged, at the price of a cluster; MSK Serverless removes the sizing and caps the throughput. Amazon MQ is RabbitMQ or ActiveMQ for a workload that already speaks AMQP. Spring reaches SQS and SNS through Spring Cloud AWS, and MSK through the same Spring Kafka as any broker.
The same concepts on Azure and GCP
Every service in this course has a counterpart, and the concepts transfer whole; the names do not:
| Concept | AWS | Azure | GCP |
|---|---|---|---|
| Identity for a service | IAM role | Managed identity | Service account |
| Private network | VPC, subnets, security groups | VNet, subnets, NSGs | VPC, subnets, firewall rules |
| Virtual machines | EC2, Auto Scaling group | Virtual Machines, VM Scale Set | Compute Engine, managed instance group |
| Containers without a cluster | ECS on Fargate | Container Apps | Cloud Run |
| Managed Kubernetes | EKS | AKS | GKE |
| Functions | Lambda | Azure Functions | Cloud Functions / Cloud Run |
| Object storage | S3 | Blob Storage | Cloud Storage |
| Managed PostgreSQL | RDS, Aurora | Azure Database for PostgreSQL | Cloud SQL, AlloyDB |
| Queue / pub-sub / streams | SQS / SNS / MSK | Service Bus / Event Grid / Event Hubs | Pub/Sub / Pub/Sub / Managed Kafka |
| HTTP load balancer | ALB | Application Gateway | Cloud Load Balancing |
| Metrics, logs, alarms | CloudWatch | Azure Monitor | Cloud Monitoring, Cloud Logging |
| Secrets | Secrets Manager | Key Vault | Secret Manager |
The judgement transfers too: identity through roles rather than keys, everything private behind one load balancer, a managed database with the pool sized to it, object storage through signed URLs, and a bill read by tag. What differs is the defaults and the sharp edges, which is why a platform team's guidance is worth more than the vendor's tutorial.
Choosing, for a Spring Boot service
| The service | Runs on | Files in | Database |
|---|---|---|---|
| one or two, small team, full control | EC2 + systemd, an ASG for two | S3 via presigned URLs | RDS PostgreSQL, Multi-AZ |
| several containerised services | ECS on Fargate | S3 | RDS, with RDS Proxy if the pool arithmetic demands |
| an organisation on Kubernetes | EKS | S3 | RDS or Aurora |
| event glue, spiky, infrequent | Lambda with SnapStart | S3 events in | DynamoDB, or RDS through the proxy |
Every row has S3 for files and a managed database, because those are the two places where the operational work is largest and least differentiating. The compute choice is the one that follows the team.