Spectre
// PUBLISHED22.08.26
// TIME9 MINS
// TAGS
#AWS#CLOUD-NATIVE#DEPLOYMENT#STARTUP
// AUTHOR
Spectre Command

T

he phrase "deploy to AWS" is doing a lot of work. It sounds like a single decision when it's actually twenty decisions stacked on top of each other. Which compute service? Which database tier? How does your container get built? Where do secrets live? Who can deploy, and how? Most engineering teams figure this out incrementally, adding pieces as problems appear, until they have something that works but nobody fully understands. That's not a crisis until someone needs to debug it at 11pm or a new engineer joins and can't figure out how to run a deployment.

Cloud-native on AWS isn't a product. It's a set of patterns ways of composing AWS services so your application is observable, reproducible, and survivable. This post walks through what that actually looks like for a startup: the core services, the decisions you can't defer, and the parts that will surprise you the first time you do this properly.


What "Cloud-Native" Actually Means in Practice

The term gets used loosely. Cloud-native means your application is designed to run on cloud infrastructure in a way that takes advantage of what cloud infrastructure offers: horizontal scaling, managed services, infrastructure-as-code, and failure tolerance built in rather than bolted on.

In concrete terms, a cloud-native application on AWS has a few defining characteristics. It runs in containers. State database, files, sessions lives outside the application process so any instance can be replaced without data loss. Configuration and secrets are injected at runtime, not baked into the image. Deployments are automated and repeatable: the same pipeline that deploys to staging deploys to production, with the same artifact. Infrastructure is defined in code, not clicked together in a console.

None of that is radical. It's just the consequence of treating your production environment as something that can fail, scale, and change which it will.

The alternative is what most teams have after a year of organic growth: an EC2 instance someone SSHed into and configured manually, a deployment process that's a bash script someone wrote that nobody runs the same way twice, secrets in a .env file committed to a private repo, and infrastructure that can't be rebuilt from scratch in under a day. That's not a judgement. It's the natural result of prioritising shipping over operations. But it compounds.


The Core AWS Services You Actually Need

AWS has over 200 services. The number that matter for deploying a standard web application is much smaller.

Compute: ECS on Fargate. For most startups, ECS (Elastic Container Service) running on Fargate is the right starting point. You define your application as a container, specify how much CPU and memory it needs, and Fargate handles the underlying EC2 instances. You never SSH into a server. You never patch an operating system. You scale by changing a number. ECS also integrates cleanly with the rest of the AWS ecosystem: IAM for permissions, CloudWatch for logs, ALB for load balancing, ECR for storing your container images.

EC2 directly is appropriate when you have workloads that need specific hardware (GPU instances for ML inference, for example) or when you need more control over the host environment than Fargate provides. For a typical API backend, it adds operational overhead without meaningful benefit.

Kubernetes on EKS is a different category. It's the right tool for teams running multiple services at significant scale who need fine-grained scheduling, custom resource definitions, and the full Kubernetes ecosystem. It's the wrong tool for a 5-engineer startup. The operational complexity is real and the learning curve is steep. Start with ECS, migrate to EKS when ECS is the bottleneck not before.

Database: RDS with a read replica. PostgreSQL on RDS with automated backups enabled and a read replica in a separate availability zone. Automated backups give you point-in-time recovery. The read replica gives you failover and the option to route read-heavy queries away from the primary. This setup handles the majority of startup database workloads up to a significant scale, and it's the baseline from which you add complexity as actual metrics demand it.

Networking: VPC with public and private subnets. Your database and internal services should sit in private subnets with no direct internet access. Your load balancer sits in public subnets. Application containers run in private subnets and communicate with the internet through a NAT gateway. This is the standard VPC architecture for a reason: it minimises the attack surface without adding application-level complexity.

Secrets: AWS Secrets Manager. Credentials, API keys, database passwords none of these belong in environment variables baked into your container image or stored in your repository. Secrets Manager stores them encrypted and your application retrieves them at startup via the AWS SDK, using an IAM role that grants only the permissions needed. Parameter Store works for non-secret configuration. Use both.

Load Balancing: ALB. An Application Load Balancer in front of your ECS service handles SSL termination, health checks, and traffic distribution across container instances. It also enables weighted routing for canary deployments: send 10% of traffic to the new version, monitor error rates, then shift 100%. That pattern is worth more than most teams realise until they've done a bad deployment without it.

Container Registry: ECR. AWS Elastic Container Registry stores your Docker images. Your CI/CD pipeline builds the image, pushes to ECR, and ECS pulls from ECR during deployment. Private by default, integrated with IAM, co-located with your ECS cluster so image pulls are fast.


CI/CD: The Part That Makes Everything Else Work

The infrastructure described above is useless without a deployment pipeline that uses it reliably. A cloud-native deployment isn't complete until a code push to main automatically builds, tests, and deploys without human intervention.

The minimal viable pipeline for AWS:

1. Developer pushes to main (or merges a PR)
2. CI runs tests unit, integration, linting
3. On pass: Docker image built and tagged with the commit SHA
4. Image pushed to ECR
5. ECS service updated to use the new image tag
6. ECS performs a rolling deployment: new tasks start, old tasks drain
7. ALB health checks confirm new tasks are healthy before old tasks terminate
8. Pipeline reports success or failure

GitHub Actions handles this cleanly with the official AWS actions for ECR push and ECS deploy. The whole pipeline from push to live production deployment should run in under 10 minutes for a typical service. If it's taking 30 minutes, something in the build or test stage needs attention.

The most common gap: no defined rollback procedure. If the new tasks fail health checks, ECS stops the deployment and keeps the old tasks running that's automatic. What's not automatic is knowing when to roll back after a deployment that technically succeeded but introduced a regression. Define the rollback procedure before you need it, not during an incident.


The Part Most Teams Get Wrong: IAM

AWS Identity and Access Management controls what every service, user, and automated process can do inside your AWS account. It's also the most consistently misunderstood part of running anything on AWS.

The common mistake is using overly broad permissions because it's faster. Your ECS task gets an IAM role with s3:* instead of s3:GetObject on the specific bucket it needs. Your CI/CD pipeline gets AdministratorAccess because figuring out the exact permissions was slower. These choices feel harmless until a compromised dependency, a misconfigured public S3 bucket, or a leaked credential turns into a serious incident.

Least-privilege IAM is not optional. It's the single most impactful security practice for AWS deployments. Every ECS task role, every Lambda execution role, every CI/CD IAM user should have exactly the permissions it needs and nothing else. AWS provides IAM Access Analyzer to identify overly permissive policies. Use it.

Two specific patterns worth knowing: the ECS task role (the role your running container assumes, controlling what your application code can call in AWS) is separate from the ECS task execution role (the role ECS itself uses to pull images from ECR and retrieve secrets from Secrets Manager). Conflating them causes permission errors that are frustrating to debug because the symptoms look identical.


Infrastructure as Code: Don't Skip This

Every AWS resource described above should be defined in code. Not clicked together in the console. Not documented in a runbook. In code that can be run to reproduce the environment exactly.

The practical options: Terraform (most commonly used, large ecosystem, works across cloud providers), AWS CDK (write infrastructure in TypeScript or Python, compiles to CloudFormation, feels like application code), or AWS CloudFormation directly (verbose but native to AWS). For most startups, Terraform or CDK are better than raw CloudFormation.

Why it matters: when something breaks in production, you need to reason about infrastructure state. When you need a staging environment that mirrors production, you run the same code against a different variable set. When an engineer leaves, the infrastructure knowledge doesn't leave with them. Infrastructure-as-code is not a nice-to-have for teams that take uptime seriously.

Write it before your production environment exists. Retrofitting IaC onto an existing manually-configured environment is a meaningful project. Starting with it adds perhaps two days of setup and saves weeks of future pain.


A Real Example: Series A Fintech Moving Off a Single EC2

A fintech startup in Jakarta came to us after two years running their entire backend API, background jobs, admin panel on a single large EC2 instance. One engineer held the SSH credentials. Deployments were git pull && pm2 restart. The database was on the same machine.

The migration to cloud-native AWS ran in parallel with product development over six weeks. Target state: ECS Fargate for the API and background job workers (separate services, separate scaling), RDS PostgreSQL with a read replica, secrets in Secrets Manager, a GitHub Actions pipeline, ALB with SSL termination, and Terraform for all of it.

What took the most time wasn't infrastructure setup. It was externalising state. The application had accumulated assumptions about the local filesystem uploaded files written to disk, a SQLite cache file, log files parsed by a background process. None of that survives containerisation. Migrating file storage to S3 and replacing the filesystem cache with Redis took three of the six weeks.

The single EC2 instance cost $180/month. The cloud-native setup costs $380/month. They have a real deployment pipeline, infrastructure reproducible from scratch in under an hour, and an on-call rotation that isn't "call the one engineer who knows the root password."

The cost increase was worth it. But knowing what the increase was buying before they started mattered.


FAQ

Q: Should a startup use ECS or EKS? A: ECS unless you have a clear reason for EKS. EKS adds real operational complexity control plane management, node groups, networking plugins, the full Kubernetes API surface. The capabilities it provides over ECS matter at scale. At early stage, they're complexity without payoff. Start with ECS Fargate, migrate when ECS is genuinely limiting you, not because a job listing mentioned Kubernetes.

Q: How do you handle database migrations in a containerised deployment? A: Run migrations as a separate step before the new application containers start. In ECS, this is a short-lived task that runs the migration script and exits. Your pipeline runs the migration task, waits for success, then updates the ECS service. If the migration fails, the old service keeps running. Never run migrations inside application startup multiple containers starting simultaneously creates race conditions.

Q: What's the minimum team size to justify a proper cloud-native AWS setup? A: It's less about team size and more about uptime expectations and product stage. A solo founder building an MVP can use Railway or Render. A startup with paying customers and an SLA needs proper infrastructure regardless of team size. The right question: what does one hour of downtime cost? If the answer exceeds the monthly AWS bill, the investment has already justified itself.

Q: How do you manage AWS costs as a startup? A: Set billing alerts from day one at $100, $500, and your budget limit. Review Cost Explorer weekly. The most common cost surprises are NAT Gateway data transfer fees, forgotten development environments, and cross-availability-zone data transfer charges. Once you have a stable baseline workload, Reserved Instances or Savings Plans cut costs 30-40% compared to on-demand pricing worth doing after two to three months of consistent usage data.

Q: What's the most common AWS deployment mistake you see at startups? A: Building directly in production with no staging environment. It feels faster early on. It becomes a liability the first time a schema migration or config change causes an outage. A staging environment doesn't need to match production in size a smaller RDS instance and fewer ECS tasks is fine. What matters is that deployments go through staging before production, and that staging uses the same infrastructure code.


Getting cloud-native deployment right from the start is genuinely faster than doing it wrong and fixing it under pressure. The setup described here isn't over-engineered for a funded startup it's the baseline that makes everything after it manageable. The teams we see struggling with AWS aren't struggling because it's too complex. They're struggling because they accumulated twelve small shortcuts that each seemed fine at the time.

If you're standing up AWS infrastructure for the first time, or inheriting something that's grown beyond what anyone fully understands, we do this kind of architecture work at SpectreDev often before writing a single line of application code.

External Documentation:

// END_OF_LOGSPECTRE_SYSTEMS_V1

Is your current architecture slowing you down?

Stop guessing where the bottlenecks are. We partner with founders and CTOs to audit technical debt and execute zero-downtime system rewrites.

Book an Architecture Audit