Skip to content

Containers on AWS with SST

Create and deploy a container to AWS with SST.

We are going to build Express app in a container, add an S3 Bucket for file uploads, and deploy it to AWS using SST.

Before you get started:

  1. Configure your AWS credentials
  2. Install the SST CLI

1. Create a project

Let’s start by creating our Express app.

Terminal window
mkdir my-express-app && cd my-express-app
npm init -y
npm install express

Init Express

Create your app by adding an index.mjs to the root.

import express from "express";
const PORT = 80;
const app = express();
app.get("/", (req, res) => {
res.send("Hello World!")
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);

Init SST

Now let’s initialize SST in our app. Make sure you have the CLI installed.

Terminal window
sst init

This’ll create a sst.config.ts file in your project root. And add a dev script to your package.json.

"scripts": {
"dev": "sst dev \"node --watch index.mjs\""

2. Add a Cluster

To deploy our Express app, let’s add an AWS Fargate container with Amazon ECS. Update your sst.config.ts.

async run() {
const vpc = new"MyVpc");
const cluster = new"MyCluster", { vpc });
cluster.addService("MyService", {
public: {
ports: [
{ listen: "80/http" },

This creates a VPC, uses it for a new ECS Cluster, adds a Fargate service to it, and exposes it through http.

Create a Dockerfile

Let’s add a Dockerfile in the root with the following.

FROM node:18-bullseye-slim
COPY package.json /app
RUN npm install
COPY index.mjs /app
ENTRYPOINT ["node", "index.mjs"]

3. Add an S3 Bucket

Let’s add a public S3 Bucket for file uploads. Update your sst.config.ts.

const bucket = new"MyBucket", {
public: true

Now, link the bucket to the container service.

cluster.addService("MyService", {
public: {
ports: [
{ listen: "80/http" },
link: [bucket],

This will allow us to reference the bucket in our Express app.

Start dev mode

Start your app in dev mode.

Terminal window
npm run dev

This will deploy your resources and start your Express app locally.

Server is running on http://localhost:80

4. Upload a file

We want the / route of our API to upload a file to our S3 Bucket. Let’s start by installing the npm packages we’ll use for the upload.

Terminal window
npm install multer @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner

Add the relevant imports.

import multer from "multer";
import { Resource } from "sst";
import { Upload } from "@aws-sdk/lib-storage";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, GetObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
const s3 = new S3Client({});
const upload = multer({ storage: multer.memoryStorage() });

And add the route to your index.mjs.

index.mjs"/", upload.single("file"), async (req, res) => {
const file = req.file;
const params = {
Key: file.originalname,
Body: file.buffer
const upload = new Upload({
client: s3,
await upload.done();
res.status(200).send("File uploaded successfully.");

5. Download a file

We want the /latest route of our app to generate a pre-signed URL to download the last uploaded file in our S3 Bucket. Add this to your index.mjs.

app.get("/latest", async (req, res) => {
const objects = await s3.send(
new ListObjectsV2Command({
const latestFile = objects.Contents.sort((a, b) => b.LastModified - a.LastModified)[0];
const command = new GetObjectCommand({
Key: latestFile.Key,
const url = await getSignedUrl(s3, command);

Test your app

Let’s try uploading a file from your project root.

Terminal window
curl -F file=@package.json http://localhost:80

Now head over to http://localhost:80/latest in your browser and it’ll download the file you just uploaded.

6. Deploy your app

Now let’s deploy your app.

Terminal window
sst deploy

This’ll give the URL of your Express app deployed as a Fargate service.

Terminal window