Three ways to run the same Next.js App Router app on AWS, side by side: SST (OpenNext under the hood), Flightcontrol (ECS Fargate), and plain Docker.
The app itself is a set of small routes that each exercise one App Router behaviour - the data-fetching and caching strategies, revalidation, draft mode, route handlers, dynamic OG images - with an MDX note next to each one explaining what it is meant to prove. That makes it usable as a probe: deploy it on a platform and see which behaviours actually survive.
Companion repo to Next.js hosting options, compared.
| Path | What it is |
|---|---|
Dockerfile.standalone |
multi-stage build on next build with output: 'standalone' - the small image |
Dockerfile.default |
the same build without standalone output, for comparison |
sst.config.ts |
SST deployment, which wraps OpenNext |
flightcontrol.json |
Flightcontrol environment and Fargate service definition |
app/ |
the routes under test, each with a doc.mdx |
SST uses OpenNext under the hood: it takes the Next.js build output and repackages it for a functions-as-a-service platform.
Configure your AWS account first, as described in the SST setup docs. To add SST to an existing Next.js project:
npx create-sst@latestThat writes an sst.config.ts where you declare the resources SST should create in AWS:
import { SSTConfig } from 'sst'
import { NextjsSite } from 'sst/constructs'
export default {
config(_input) {
return {
name: 'nextjs-selfhosting-aws',
region: 'eu-north-1',
}
},
stacks(app) {
if (app.stage !== 'prod') {
app.setDefaultRemovalPolicy('destroy')
}
app.stack(function Site({ stack }) {
const site = new NextjsSite(stack, 'nextjs-selfhosting-aws')
stack.addOutputs({
SiteUrl: site.url,
})
})
},
} satisfies SSTConfigLocal dev against real AWS resources (how it works):
npx sst devDeploy:
npx sst deploy --stage prodBehind the scenes SST compiles the constructs to CloudFormation, packages the frontend assets and the functions, uploads them, and creates the infrastructure.
You need a Flightcontrol account with an AWS account linked to it. Everything can be configured through the UI, or declared in flightcontrol.json:
{
"environments": [
{
"id": "production",
"name": "Production",
"region": "eu-central-1",
"source": {
"branch": "main"
},
"services": [
{
"id": "nextjs-selfhosting-aws-fc",
"name": "Next.js self-hosting demo",
"type": "fargate",
"cpu": 1,
"memory": 2,
"minInstances": 1,
"maxInstances": 1,
"buildCommand": "npm run build",
"startCommand": "npm run start"
}
]
}
]
}Flightcontrol runs on ECS Fargate, so the resulting infrastructure is inspectable in the AWS console like anything else.
docker build -f Dockerfile.standalone -t nextjs-selfhosting-aws .
docker run -p 3000:3000 nextjs-selfhosting-awsDockerfile.default builds the same app without standalone output. Comparing the two image sizes is the point of keeping both.
For the baseline everything else is measured against, the Vercel platform needs no configuration from this repo. See the Next.js deployment docs.
Built by FocusReactive, a headless CMS and eCommerce engineering agency. We audit and run self-hosted Next.js in production - Next.js hosting options, compared and CDN caching for self-hosted Next.js.