Deploy Remix to AWS Lightsail with Docker
Read the full tutorial here. Requirements:
- Install Docker Desktop
- Create an AWS account
- Install AWS CLI
- Install the Lightsail container services plugin
Find and replace:
saasrock-demo-service
with your AWS Lightsail service namesaasrock-demo-image
with your image nameus-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:
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.
And if you check your Amazon Lightsail dashboard, you should see your container service:
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 depsWORKDIR /myappADD package.json ./
RUN npm install --production=false --legacy-peer-deps# Setup production node_modules
FROM base as production-depsWORKDIR /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 buildWORKDIR /myappCOPY --from=deps /myapp/node_modules /myapp/node_modulesADD prisma .
RUN npx prisma generateADD . .
RUN npm run build# Finally, build the production image with minimal footprint
FROM baseENV 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/.prismaCOPY --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/prismaRUN 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!