Deploy Remix to AWS Lightsail with Docker

Alexandro Martinez
4 min readMay 30, 2024

--

Deploy Remix to AWS

Read the full tutorial here. Requirements:

Find and replace:

  • saasrock-demo-service with your AWS Lightsail service name
  • saasrock-demo-image with your image name
  • us-east-1 with your preferred region

Deployment Steps

💿 1. Build the image

docker build -t saasrock-demo-image .

💿 2. Run the image locally

docker run -p 8080:8080 --env-file .env.demo saasrock-demo-image:latest

💿 3. Create a Lightsail container service

aws lightsail create-container-service --region us-east-1 --service-name saasrock-demo-service --power nano --scale 1

This is creating a Nano instance ($7/month) with 1 instance:

Amazon Lightsail Container Sizes

You can check the status of the service with:

aws lightsail get-container-services --region us-east-1 --service-name saasrock-demo-service --query "containerServices[].state"

💿 4. Push the image to the AWS Lightsail container service

aws lightsail push-container-image --region us-east-1 --service-name saasrock-demo-service --label latest --image saasrock-demo-image:latest

💿 5. Create an AWS configuration file aws-lightsail-containers.json

{
"serviceName": "saasrock-demo-service",
"containers": {
"saasrock-demo-service": {
"image": "saasrock-demo-image:latest",
"environment": {
"APP_NAME": "SaasRock Dev AWS Lightsail"
// ...
},
"ports": {
"8080": "HTTP"
}
}
},
"publicEndpoint": {
"containerName": "saasrock-demo-service",
"containerPort": 8080
}
}

⚠️ IMPORTANT: Add this file to your .gitignore to avoid sharing sensitive information.

💿 6. Deploy the image to the AWS Lightsail container service

aws lightsail create-container-service-deployment --region us-east-1 --cli-input-json file://aws-lightsail-containers.json

Check the status of the deployment with:

aws lightsail get-container-services --region us-east-1  --query "containerServices[].nextDeployment.state"

Once it’s deployed, grab the public endpoint with:

aws lightsail get-container-services --region us-east-1 --service-name saasrock-demo-service --query "containerServices[].url"

And that’s it! You can access your app at the public endpoint provided by AWS Lightsail.

SaasRock deployed to AWS Lightsail

And if you check your Amazon Lightsail dashboard, you should see your container service:

AWS deployed container

Deploying Updates

💿 1. Build the image as in step 1

docker build -t saasrock-demo-image .

💿 2. Push the image as in step 4

aws lightsail push-container-image --region us-east-1 --service-name saasrock-demo-service --label latest --image saasrock-demo-image:latest

💿 3. Grab the new image tag

Digest: sha256:5723533c861348b69369fb79d85c6b203713c33b49f0b1b813d9d9bf71454732
Image "saasrock-demo-image:latest" registered.
Refer to this image as ":saasrock-demo-service.latest.4" in deployments.

In this case, the image tag is saasrock-demo-service.latest.4.

💿 4. Change the image tag in the aws-lightsail-containers.json file

{
...
"containers": {
"saasrock-demo-service": {
"image": "UPDATE_IMAGE_TAG_HERE",
...
}

💿 5. Deploy the image as in step 6

aws lightsail create-container-service-deployment --region us-east-1 --cli-input-json file://aws-lightsail-containers.json

Wait for the deployment to finish, you can check the status with:

aws lightsail get-container-services --region us-east-1 --service-name saasrock-demo-service --query "containerServices[].state"

And that’s it! Your app is now updated.

ℹ️ NOTE: If you forgot the URL of your app, you can get it with:

aws lightsail get-container-services --region us-east-1 --service-name saasrock-demo-service --query "containerServices[].url"

Files Reference

Dockerfile:

#!/bin/bash# base node image
FROM --platform=linux/amd64 node:18-bookworm-slim as base
# set for base and all layer that inherit from it
ENV NODE_ENV production
# Install openssl for Prisma
RUN apt-get update && apt-get install -y openssl
# Install all node_modules, including dev dependencies
FROM base as deps
WORKDIR /myappADD package.json ./
RUN npm install --production=false --legacy-peer-deps
# Setup production node_modules
FROM base as production-deps
WORKDIR /myappCOPY --from=deps /myapp/node_modules /myapp/node_modules
ADD package.json ./
RUN npm prune --production --legacy-peer-deps
# Build the app
FROM base as build
WORKDIR /myappCOPY --from=deps /myapp/node_modules /myapp/node_modulesADD prisma .
RUN npx prisma generate
ADD . .
RUN npm run build
# Finally, build the production image with minimal footprint
FROM base
ENV PORT="8080"
ENV NODE_ENV="production"
WORKDIR /myappCOPY --from=production-deps /myapp/node_modules /myapp/node_modules
COPY --from=build /myapp/node_modules/.prisma /myapp/node_modules/.prisma
COPY --from=build /myapp/build /myapp/build
COPY --from=build /myapp/public /myapp/public
COPY --from=build /myapp/package.json /myapp/package.json
COPY --from=build /myapp/start.sh /myapp/start.sh
COPY --from=build /myapp/prisma /myapp/prisma
RUN chmod +x /myapp/start.shENTRYPOINT [ "./start.sh" ]

start.sh:

#!/bin/sh
npm run start

.dockerignore

/node_modules
*.log
.DS_Store
.env
/.cache
/public/build
/build

package.json:

{
...
"scripts": {
...
"build": "remix vite:build",
"start": "remix-serve ./build/index.js"
},
"dependencies": {
...
},
"devDependencies": {
...
}
}

vite.config.ts:

import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import remixConfig from "./remix.config";
import tsconfigPaths from "vite-tsconfig-paths";
import { vercelPreset } from "@vercel/remix/vite";

export default defineConfig({
server: { port: 3000 },
plugins: [
tsconfigPaths(),
remix({
...remixConfig,
presets: [vercelPreset],
}),
],
ssr: {
noExternal: ["remix-i18next"],
},
resolve: {
alias: {
".prisma/client/index-browser": "./node_modules/.prisma/client/index-browser.js",
},
},
});

So now I can finally say that saasrock.com officially supports:

Subscribe to SaasRock’s newsletter for more like this!

--

--

Alexandro Martinez

Building SaasRock, The One-Man SaaS Framework built with Remix + Tailwind CSS