Cloud Deployment Roadmap
Kubernetes is not the right answer for every team. It solves real problems at scale — but it introduces significant operational complexity and cost that can sink a small team or startup before they ship their first feature. This class maps the deployment landscape and provides a decision framework: given your team size, service count, and operational maturity, what is the right infrastructure choice right now, and how do you grow into the next tier?
1. The Spectrum of Managed Infrastructure
Cloud providers have progressively abstracted away infrastructure concerns. The tradeoff is always the same: less control, less operational burden.
flowchart LR
vm["EC2 / VMs\n(full control,\nfull burden)"]
ecs["ECS Fargate\n(containers,\nno nodes)"]
eks["EKS / K8s\n(full orchestration,\nhigh ops)"]
apprunner["App Runner\n(git → URL,\nzero config)"]
paas["Railway / Fly.io\n(PaaS,\ndeveloper-first)"]
lightsail["Lightsail\n(VPS-style,\nsimplest AWS)"]
vm -->|"more managed →"| ecs
ecs -->|"more managed →"| eks
lightsail -->|"more managed →"| apprunner
apprunner -->|"more managed →"| paas Two paths, not one axis
There is no single spectrum here. ECS is not "more managed" than App Runner — they target different use cases. The diagram shows two paths: the AWS-native path (VM → ECS → EKS) and the developer-experience path (Lightsail → App Runner → PaaS). Your team's context determines which path is relevant.
| Platform | What it hides | What you manage | Best for | Est. monthly cost (3 services) |
|---|---|---|---|---|
| AWS Lightsail | Hardware, networking | OS, runtime, scaling | First cloud deployment, student projects | $15–50 |
| Railway / Render | Everything except code | Env vars, domains | Startups, prototypes, side projects | $5–30 |
| Fly.io | Infrastructure | Dockerfile, regions | Global low-latency apps, edge deployments | $5–50 |
| AWS App Runner | Containers, scaling | Container image, env vars | Stateless microservices on AWS | $25–80 |
| AWS ECS Fargate | EC2 nodes | Task definitions, service scaling, ALB | Medium-scale, AWS-native, production | $60–200 |
| AWS EKS | Control plane | Node groups, networking, add-ons | Large teams, complex workloads, multi-tenant | $150–500+ |
| Self-hosted K3s | Cloud provider | All K8s operations | On-prem, homelab, cost-sensitive | Hardware only |
2. Decision Roadmap by Team Size
Fewer than 10 services? Start here.
flowchart TD
A["Need custom domains + HTTPS?"]
B["Railway / Render\n(free tier)"]
C["Need a managed database?"]
D["Fly.io"]
E["AWS Lightsail Containers\nor AWS App Runner"]
A -->|No| B
A -->|Yes| C
C -->|No| D
C -->|Yes| E Key recommendation: Railway for the fastest time-to-live — connect a GitHub repo and you have a live URL in minutes. AWS App Runner when you are already in the AWS ecosystem and want a managed container platform without ECS complexity.
EKS at this scale
The EKS control plane alone costs **\(72/month**. Add a minimum node group (\)50–100/month) and you are already spending more than your entire infrastructure should cost for a small team. Kubernetes is not an option here — it is a liability.
10–50 services. You need production-grade infrastructure but probably not a full platform engineering team.
flowchart TD
A["Dedicated ops / platform engineer?"]
B["ECS Fargate + ALB + RDS\n(serverless containers, no nodes)"]
C["EKS (managed node groups)\nor ECS with more control"]
A -->|No| B
A -->|Yes| C Why ECS Fargate for medium teams: no EC2 nodes to patch, scales to zero, integrates natively with AWS IAM, Secrets Manager, and ECR. You lose the Kubernetes ecosystem but gain roughly 40% cost reduction and dramatically simpler day-to-day operations.
| ECS Fargate | EKS | |
|---|---|---|
| Node management | None (serverless) | You manage node groups |
| Learning curve | Low (task definitions) | High (full K8s API) |
| Ecosystem (Helm, CRDs) | Limited | Full |
| Cost at 10 services | ~$120/month | ~$300/month |
| Multi-cloud portability | AWS-only | Portable |
| GitOps (ArgoCD, Flux) | Limited | First-class |
50+ services. EKS is the right answer — now the operational cost is justified.
At this scale, you need:
- Managed node groups (or Karpenter for bin-packing autoscaling)
- ArgoCD for GitOps — declarative cluster state from Git
- AWS Load Balancer Controller — provision ALB/NLB from Kubernetes Ingress resources
- external-secrets-operator — sync Vault or AWS Secrets Manager secrets into Kubernetes Secrets
- Separate clusters per environment — dev, staging, prod each get their own EKS cluster
| Add-on | Purpose |
|---|---|
| Karpenter | Node autoscaling — provision exactly the right instance types for pending pods |
| ArgoCD | GitOps — declarative cluster state reconciled from Git |
| AWS Load Balancer Controller | Provision ALB/NLB from Kubernetes Ingress resources |
| External Secrets Operator | Sync Vault/Secrets Manager secrets into Kubernetes Secrets |
| Metrics Server | Enables HPA (Horizontal Pod Autoscaler) |
| Cluster Autoscaler | Alternative to Karpenter for node scaling |
3. Platform Deep-Dives
AWS Lightsail Containers
Lightsail wraps containers in an abstraction that feels like Heroku: push an image, set environment variables, choose an instance size, get a public HTTPS endpoint. No VPCs, security groups, or IAM policies to configure.
$ aws lightsail create-container-service --service-name my-api \
--power small --scale 1
$ aws lightsail create-container-service-deployment \
--service-name my-api \
--containers '{"my-api":{"image":"my-registry/my-api:latest","ports":{"8080":"HTTP"}}}' \
--public-endpoint '{"containerName":"my-api","containerPort":8080}'
Lightsail networking limitations
Lightsail does not use standard AWS VPC networking. Services deployed on Lightsail cannot talk to RDS instances in a normal VPC without Lightsail VPC peering — an extra configuration step many tutorials skip. There is also no service-to-service discovery: each service gets its own public URL.
AWS App Runner
App Runner takes a container image (or a Git repository with a Dockerfile) and provisions a fully managed HTTPS endpoint that auto-scales from 0 to N instances with no server management.
version: 1.0
runtime: corretto17
build:
commands:
build:
- mvn clean package -DskipTests
run:
command: java -jar target/app.jar
network:
port: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: prod
VPC Connector required for RDS
App Runner runs in an AWS-managed VPC by default — it has no network path to resources in your own VPC. Connecting to an RDS instance requires configuring an App Runner VPC Connector, which attaches your App Runner service to a subnet in your VPC. This is straightforward but not automatic.
Railway
Railway provides the simplest developer experience in the ecosystem: connect a GitHub repo, Railway detects the language and framework, builds and deploys automatically.
What Railway provides out of the box:
- Automatic HTTPS with custom domains
- Built-in Postgres, Redis, and MongoDB as add-on services
- Deploy on push — no CI/CD pipeline needed initially
- Dashboard with logs, metrics, and environment variable management
Railway trade-offs at scale
Railway gives you very little control over networking — there is no VPC, no private subnets, no security group equivalent. There is also no Kubernetes ecosystem (no Helm, no CRDs, no GitOps tooling). Vendor lock-in becomes a real risk once you have more than 10–15 services and complex inter-service networking requirements.
Fly.io
Fly.io runs containers on edge nodes distributed worldwide, close to your users. A fly.toml config file defines the application; flyctl deploy handles building and deploying globally.
app = "my-order-service"
primary_region = "gru" # São Paulo
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 8080
force_https = true
[env]
SPRING_PROFILES_ACTIVE = "prod"
Strong suit: Fly.io places your containers in regions close to your users — deploying to gru (São Paulo) means ~15ms latency for most Brazilian users, and you can add iad (Virginia) or lhr (London) with a single flag to serve global traffic.
Weak suit: Stateful services are more complex than on AWS. Fly has Postgres support but it behaves differently from RDS — backup management, replication, and failover require more manual attention.
4. Kubernetes Alternatives for Orchestration
If you want Kubernetes features but not EKS cost or complexity, these distributions are worth knowing:
K3s — Lightweight Kubernetes (< 100 MB binary) by Rancher. Implements the same API as upstream Kubernetes. Ideal for on-premises deployments, edge devices, and development clusters. The Minikube environment used in this course is effectively a single-node K3s equivalent.
K0s — Zero-friction Kubernetes. A single binary with no external dependencies (no external etcd, no separate control plane binary). Common choice for CI/CD environments and on-premises production where K3s's Rancher tooling is unwanted.
Kind (Kubernetes in Docker) — Runs a full Kubernetes cluster inside Docker containers. The standard choice for local integration testing and validating Kubernetes manifests before pushing to a real cluster.
| Minikube | K3s | K0s | Kind | |
|---|---|---|---|---|
| Use case | Local dev | On-prem / edge prod | On-prem prod | CI/CD testing |
| Full K8s API | ✓ | ✓ | ✓ | ✓ |
| Production-grade | ✗ | ✓ | ✓ | ✗ |
| Resource requirements | Medium | Very low | Very low | Low |
5. Cost Comparison
Monthly cost estimates for running three microservices (account, order, gateway) across deployment platforms. Costs vary significantly with traffic and instance size — these are representative baselines for moderate traffic.
6. Migration Path
The right answer changes as you grow
Start simple and migrate deliberately. Each migration should be driven by a concrete pain point, not speculation about future scale. A working app on Railway is worth more than a perfectly architected EKS cluster with no users.
flowchart TD
start(["Idea / Prototype"])
r["Railway / Fly.io\n(Day 1 — ship fast)"]
app["AWS App Runner\n(Month 3 — AWS-native, stateless)"]
ecs["AWS ECS Fargate\n(Year 1 — production, stateful)"]
eks["AWS EKS\n(Year 2+ — scale, team grows)"]
start --> r
r -->|"Need AWS services\nor compliance"| app
app -->|"Need stateful services,\nfine-grained control"| ecs
ecs -->|"Multi-team, 50+ services,\nGitOps required"| eks
style r fill:#4CAF50,color:#fff
style app fill:#FF9800,color:#fff
style ecs fill:#FF5722,color:#fff
style eks fill:#F44336,color:#fff Avoid premature optimization
The cost of migrating from Railway to ECS at 20 services is a few weeks of focused engineering work. The cost of operating EKS for a 3-person startup for a year is 40+ hours of platform ops work that produces no user-facing features. Start simple. Migrate when the pain is real, measurable, and no cheaper fix exists.