There are at least a dozen ways to run code on AWS, which is exactly why figuring out how to deploy to AWS feels harder than it should. The good news for 2026: the managed options are finally good enough that most people never touch a raw EC2 instance. This guide picks the right service, ships it, and keeps the bill honest.
What changed in 2026
- Managed container runners matured. AWS App Runner now covers a large share of "just run my Docker image" needs — no load balancer, no cluster, no patching.
- OIDC killed static keys. GitHub Actions authenticates to AWS with short-lived tokens via OpenID Connect. Storing
AWS_SECRET_ACCESS_KEY in CI is now a red flag, not a default.
- The CDK is the sane default for IaC. Writing infrastructure in TypeScript or Python (instead of thousands of lines of YAML) is mainstream, and it plays well with Terraform if your team prefers that.
- Cost tooling is built in, but you still have to turn it on. Budgets and anomaly alerts exist; AWS will not stop a runaway bill for you.
Pick the right service first
Do not start with "which EC2 instance size." Start with the shape of your app.
| Your app |
Best first choice |
Why |
Watch out for |
| Static site / SPA |
Amplify Hosting or S3 + CloudFront |
Global CDN, free SSL, cheap |
Cache invalidation quirks |
| One container |
App Runner |
Push image, get a URL |
Fewer knobs than ECS |
| Multiple containers |
ECS on Fargate |
No servers, real orchestration |
Task/networking learning curve |
| Event or API glue |
Lambda + API Gateway |
Scales to zero, pay per call |
Cold starts, 15-min limit |
| Full-stack framework |
Amplify or App Runner |
Managed build + deploy |
Framework version support |
| Legacy or special hardware |
EC2 |
Full control |
You own patching and uptime |
The rule that saves the most time: choose the highest-level service that fits, and drop down only when you hit a concrete limit.
The fastest path: App Runner
If your app is one container, App Runner is usually the shortest route from code to a live HTTPS URL.
# Build and push to Elastic Container Registry
aws ecr create-repository --repository-name myapp
docker build -t myapp .
docker tag myapp:latest <acct>.dkr.ecr.<region>.amazonaws.com/myapp:latest
docker push <acct>.dkr.ecr.<region>.amazonaws.com/myapp:latest
# Point App Runner at the image (or connect it to the repo for auto-deploys)
aws apprunner create-service \
--service-name myapp \
--source-configuration file://source-config.json
App Runner handles TLS, autoscaling, and health checks. For a static frontend, Amplify Hosting is the parallel one-command option, wiring a CDN and build pipeline to your Git branch.
Define it as code, not clicks
Console clicks are fine for learning and terrible for anything you need to rebuild. Describe the stack once and let it be reproducible. A minimal AWS CDK service looks like this:
import { Stack } from 'aws-cdk-lib';
import { Service, Source } from '@aws-cdk/aws-apprunner-alpha';
class AppStack extends Stack {
constructor(scope, id, props) {
super(scope, id, props);
new Service(this, 'MyApp', {
source: Source.fromEcr({
imageConfiguration: { port: 8080 },
repository: myEcrRepo,
tagOrDigest: 'latest',
}),
});
}
}
cdk deploy creates everything; cdk destroy removes it. Being able to tear down cleanly is what keeps test environments from becoming forgotten line items.
CI/CD with GitHub Actions and OIDC
Do not paste AWS keys into GitHub secrets. Create an IAM role that trusts GitHub's OIDC provider, then assume it at deploy time.
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::<acct>:role/github-deploy
aws-region: us-east-1
- run: npm ci && npm test
- run: cdk deploy --require-approval never
Tokens are short-lived and scoped, so a leaked log line cannot become a persistent breach.
Watch the bill, honestly
The surprise-bill stories are almost always the same handful of causes. Set a budget alert on day one, then avoid these:
- NAT Gateways run around the clock — use VPC endpoints or a public subnet for simple workloads.
- Idle load balancers bill hourly whether or not traffic flows. Delete the ones experiments left behind.
- Data transfer out is the quiet cost; a chatty API serving big responses can dwarf compute.
- "Free tier" is 12 months, not forever. Verify current pricing yourself before assuming anything is free — AWS pricing changes and varies by region.
FAQ
What is the single easiest way to deploy to AWS?
For a static site, Amplify Hosting. For one container, App Runner. Both go from repo to live URL without you managing servers, load balancers, or certificates.
Do I need Kubernetes (EKS) on AWS?
Almost certainly not to start. ECS on Fargate gives you real orchestration without the cluster overhead. Reach for EKS only with a dedicated platform team.
How do I keep costs predictable?
Turn on AWS Budgets with an email alert, use cost anomaly detection, tag resources by project, and destroy test stacks when done. The biggest lever is deleting idle infrastructure.
Should I still use EC2 in 2026?
Only when a managed service cannot do the job — special hardware, licensing, or legacy software. For a fresh app, EC2 is the fallback, not the default.
Where to go next
Once your app is live, secure and connect it: read API authentication explained to lock down endpoints, brush up on async/await for clean concurrency, and see the best AI coding assistants to speed up the build.