Deploying Nuxt.js SSR Apps To AWS ECS

Deploying Nuxt.js SSR Apps To AWS ECS

For the past few hours, I’ve been working on dockerizing a Nuxt.js server-side rendering app to be deployed to AWS Elastic Container Service (ECS). I hit a stone wall when environment variables are not properly injected on runtime on ECS.

In this post, I will walk us through steps to properly dockerize Nuxt.js SSR apps and share some of the lessons I learnt.

There are 2 major ways to dockerize Nuxt.js apps, and each of them has its shortcomings:

  1. Nuxt build on run: The Nuxt app is built after Docker image is built, when the container is spawned. The downside of this is the container boot time takes significantly longer as it involves building the artifacts on run. This also violates the build/release/run principle of a 12-factor app.
  2. Nuxt build on Docker build: For this, sensitive environment variables are injected during docker build stage. The upside is the boot time is much shorter now, hence, quicker revert time in case you need to revert to an older version. However, the downside is the sensitive variables are now bundled into our Docker image and it requires a9 rebuild every time a variable is updated.

For this post, I will be using the first method, “Nuxt build on run”.

How environment variables works in Nuxt.js apps?

Let’s revisit how environment variables work in Nuxt apps. Some of us may already be familiar with the env property in nuxt.config.js, which allows us to inject sensitive values from your terminal environment to the app on runtime.

// in nuxt.config.js

export default {
  env: {
    /**
      * dbUri is accessible from both client and server side.
      */
    dbUri: process.env.DB_URI  
  }
}

From the snippet above, DB_URI is read and injected to dbUri when we run the app for any purpose. ( nuxt start, nuxt build or nuxt)

When serving a production build, our environment variables are most likely different from environment to environment. Thus, if we build our app ( nuxt build) as part of the building steps of our Docker image, any sensitive environment variables that is injected when we run our Docker image ( docker run ...) will not work as the artifact has been built before these variables are injected.

How to safely inject environment variables into Dockerized Nuxt.js on runtime?

By adopting “ Nuxt build on run”, we adhere to:

Principle 1: Build and serve on runtime!

Principle 2: Never embed any sensitive value in Dockerfile

In my package.json, I added a script to build and run my app:

{
  ...
  "scripts": {
    ...
    "build": "nuxt build --modern=server",
    "start": "nuxt start",
    "prod": "yarn build && yarn start"
    ...
  }
  ...
}

To serve a production build, all we need to do is to run yarn prod.

In my Dockerfile:

FROM node:10.16.3

ENV APP_DIR /app/
WORKDIR ${APP_DIR}

COPY . ./
RUN yarn install

ENV HOST 0.0.0.0   # Insensitive environment variable

EXPOSE 3000

CMD ["yarn", "prod"]

Here we go! Our dockerized Nuxt app is ready.

We can run our Docker image using docker run or alternatively with Docker Compose. I personally use Docker Compose a lot. In my case, I created a docker-compose.production.yml that inject environment variables from the .env file in same directory.

My docker-compose.production.yml:

version: "3"
services:
  web:
    build: .
    restart: always
    env_file:
      - .env
    ports:
      - "3000:3000"

Once ready, we run docker-compose -f docker-compose.production.yml up -d to bring up our containerized Nuxt app.

How to safely inject environment variable for dockerized Nuxt.js on AWS ECS

This section is only applicable if you choose ECS as your deployment platform of choice.

In this section, I will walk us through on:

  • storing sensitive values using AWS SSM Parameter Store
  • building and pushing image to image registry
  • creating a ECS task

Step 1: Storing Sensitive Environment Variables in Parameter Store

AWS provides several products for the purpose of storing sensitive configurations, namely AWS SSM Parameter Store and AWS Secrets Manager. I use Parameter Store because of its’ free 10,000 Standard tier parameters.

To add a parameter, click “Create Parameter” in the Parameter Store Management Console:

This is image title

Fill up the form, then submit:

This is image title

We should see the parameters created:

This is image title

We will leave the parameters created for now. These will be consumed in a latter step.

Step 2: Building and Pushing Image to Image Registry

To deploy our Nuxt.js app to AWS ECS, we first require a repository to store our Docker image. For the sake of simplicity, I use AWS Elastic Container Registry (ECR).

The official guide provides a comprehensive list of steps to create a repository on AWS ECR.

Once the image repository is created, we can then build and push our image:

Once it’s pushed, we should see a similar output on our terminal:

This is image title

Navigating to our repository with AWS Management Console, we should see our image pushed:

This is image title

Step 3: Create a ECS Task

This guide details the steps to create a Task definition in ECS. It is highly suggested to read this if you are not familiar with the concepts of Task and Service in ECS before moving forward.

While creating an ECS Task with the Management Console, you can now reference to parameters created in SSM Parameter Store by using the ARN of the parameters.

This is image title

Once the Task is created, we can then run it on ECS. Navigating the the endpoint of your Task, you should be able to see your app is up and running.

Voila!

Now we have a dockerized Nuxt.js SSR app that is running on ECS with sensitive environment variables stored on SSM Parameter Store.

Have fun hacking around!

nuxt-js vue-js

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

8 Popular Websites That Use The Vue.JS Framework

In this article, we are going to list out the most popular websites using Vue JS as their frontend framework. Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.

Serverless-side rendering with Vue.js, Nuxt.js and AWS Lambda

We want the best of both worlds. The SEO boost server-side rendering provides, and the speed of a Single Page Application. All this while hosted basically for free in a serverless environment on AWS Lambda. Here’s a quick overview of what we’ll be building for you to get up to speed. Feel free to jump to the step that interests you the most.

How to build enterprise Vue.js applications with Nuxt?

Learn How to build enterprise Vue.js applications with Nuxt: Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS). Nuxt goal is to make web development powerful and performant with a great developer experience in mind.

Responsive ecommerce template built with Vue.js and Nuxt.js

Responsive ecommerce template built with Vue.js and Nuxt.js

Nuxt.js: a Minimalist Framework for Creating Universal Vue.js Apps

In this article, you'll learn how we can take advantage of Nuxt.js to build server-rendered JavaScript applications with Vue.js. Learn how to use its generate command to generate static files for our pages, and deploy them quickly via a service like Firebase Hosting.