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, make sure to configure your AWS credentials.


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.

index.mjs
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 to add the @ion part.

Terminal window
npx sst@ion init
npm install

This’ll create a sst.config.ts file in your project root.


2. Add a Cluster

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

sst.config.ts
async run() {
const vpc = new sst.aws.Vpc("MyVpc");
const cluster = new sst.aws.Cluster("MyCluster", { vpc });
cluster.addService("MyService", {
public: {
ports: [
{ listen: "80/http" },
],
dev: {
command: "node --watch index.mjs",
},
},
});
}

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

The dev.command tells SST to run our Express app locally in dev mode.


Create a Dockerfile

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

Dockerfile
FROM node:18-bullseye-slim
WORKDIR /app/
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.

sst.config.ts
const bucket = new sst.aws.Bucket("MyBucket", {
public: true
});

Now, link the bucket to the container service.

sst.config.ts
cluster.addService("MyService", {
// ...
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
npx sst dev

Once complete, click on MyService in the sidebar for your local Express app.


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.

index.ts
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
app.post("/", upload.single("file"), async (req, res) => {
const file = req.file;
const params = {
Bucket: Resource.MyBucket.name,
Key: file.originalname,
Body: file.buffer
};
const upload = new Upload({
params,
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.

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

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
npx sst deploy --stage production

You can use any stage name here but it’s good to create a new stage for production. This’ll give the URL of your Express app deployed as a Fargate service.

Terminal window
+ Complete
MyService: http://jayair-MyServiceLoadBala-592628062.us-east-1.elb.amazonaws.com

Connect the console

As a next step, you can setup the SST Console to git push to deploy your app and monitor it for any issues.

SST Console Autodeploy

You can create a free account and connect it to your AWS account.